using Cognex.VisionPro.ToolBlock;
using MahApps.Metro.Controls;
using NPOI.OpenXmlFormats.Vml;
using Opc.Ua;
using Org.BouncyCastle.Crypto;
using Prism.Events;
using Prism.Ioc;
using Prism.Mvvm;
using Prism.Regions;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Sockets;
using System.Security.Claims;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.UI.WebControls;
using System.Windows.Media;
using Team.FFFeederService.Interfaces;
using TeamAAS_VP.Core.PLCs;
using TeamAAS_VP.Core.ScrewDriver;
using TeamAAS_VP.Data;
using TeamAAS_VP.Enums;
using TeamAAS_VP.Events;
using TeamAAS_VP.Interfaces;
using TeamAAS_VP.Models;
using TeamAAS_VP.Models.PLC;
using TeamAAS_VP.Models.Torque;
using TeamAAS_VP.Resources.Languages;
using TeamAAS_VP.Views.Product;
using TouchSocket.Core;
using TouchSocket.SerialPorts;
using TouchSocket.Sockets;
using Timer = System.Threading.Timer;
namespace TeamAAS_VP.Core
{
public class Management : BindableBase
{
#region 字段
IRegionManager _regionManager;
IEventAggregator _eventAggregator;
IContainerProvider _container;
IConfigService _configService;
IRobotService _robotService;
ICameraService _cameraService;
IFeederService _feederService;
IPlcService _plcService;
ILightManagerService _lightManagerService;
IProductService _productService;
ICalibrationService _calibrationService;
IRemoteCommandService _remoteCommandService;
ISystemDatabaseService _systemDatabaseService;
IMesService _mesService;
Timer yieldtimer;
private DateTime StartTime = DateTime.Now;
SerialPortClient client;
// Pressure collection
///
/// 压力采集信号控制CTS
///
private CancellationTokenSource[] _pressureCts = new CancellationTokenSource[100];
///
/// 压力采集超时CTS
///
private CancellationTokenSource[] _pressureTimeoutCts = new CancellationTokenSource[100];
///
/// 压力采集CTS
///
private CancellationTokenSource[] _pressureLinkedCts = new CancellationTokenSource[100];
///
/// 压力数据集合
///
private List<(DateTime Timestamp, float Value)> _pressureSamples = new List<(DateTime, float)>();
///
/// 采样间隔毫秒
///
private int _pressureSampleIntervalMs = 50; //采样间隔毫秒
///
/// 超时自动停止秒数
///
private int _pressureMaxDurationSeconds = 30; //超时自动停止秒数
DateTime[] StartCtqTime = new DateTime[3];//开始采集Ctq数据的时间
///
/// 上次执行上传csv的日期
///
private DateTime _lastCompressDate = DateTime.MinValue;
///
/// 当天是否上传过csv
///
private bool _hasCompressedToday = false;
List lockResultsAll = new List();//一个产品的总结果
string productSn = "";//产品sn
DateTime StartScrewTime = DateTime.Now;//开始锁附的时间
#endregion
#region 属性
///
/// 当前顶部显示的运行模式
///
private RunDisplayMode _CurrentDisplayMode = RunDisplayMode.Unknown;
public RunDisplayMode CurrentDisplayMode
{
get { return _CurrentDisplayMode; }
set { SetProperty(ref _CurrentDisplayMode, value); }
}
private ObservableCollection _Renders;
///
/// 显示界面
///
public ObservableCollection Renders
{
get { return _Renders; }
set { SetProperty(ref _Renders, value); }
}
public BgCommunicate BgCommunicate { get; private set; }
public BgModbusTcpCommunicate BgModbusTcpCommunicate { get; private set; }
private ObservableCollection _Status = new ObservableCollection();
///
/// 状态栏集合
///
public ObservableCollection Status
{
get { return _Status; }
set { SetProperty(ref _Status, value); }
}
private Int64 _TotalQuantity;
///
/// 总产量
///
public Int64 TotalQuantity
{
get { return _TotalQuantity; }
set { SetProperty(ref _TotalQuantity, value); }
}
private Int64 _TotalQuantityToday;
///
/// 当天的总产量
///
public Int64 TotalQuantityToday
{
get { return _TotalQuantityToday; }
set { SetProperty(ref _TotalQuantityToday, value); }
}
private Int64 _CurrentUPH;
///
/// 当前的UPH
///
public Int64 CurrentUPH
{
get { return _CurrentUPH; }
set { SetProperty(ref _CurrentUPH, value); }
}
private Int64 _ThrowNumber;
///
/// 抛料数
///
public Int64 ThrowNumber
{
get { return _ThrowNumber; }
set { SetProperty(ref _ThrowNumber, value); }
}
private Int64 _NgNumber;
///
/// ng数量
///
public Int64 NgNumber
{
get { return _NgNumber; }
set { SetProperty(ref _NgNumber, value); }
}
private double _Yield;
///
/// 良率
///
public double Yield
{
get { return _Yield; }
set { SetProperty(ref _Yield, value); }
}
private XYD_ScrewDriver _ScrewDriver;
public XYD_ScrewDriver ScrewDriver
{
get { return _ScrewDriver; }
set { SetProperty(ref _ScrewDriver, value); }
}
public Dictionary BackupObject { get; set; } = new Dictionary();
private string _SignalValue = "0";
///
/// 扭力测量仪获取的最大值
///
public string SignalValue
{
get { return _SignalValue; }
set { SetProperty(ref _SignalValue, value); }
}
private NumberStatistics _NumStatistics;
///
/// 数量(批头、吸嘴、供料器)
///
public NumberStatistics NumStatistics
{
get { return _NumStatistics; }
set { SetProperty(ref _NumStatistics, value); }
}
private double _CTtime;
///
/// ct
///
public double CTtime
{
get { return _CTtime; }
set { SetProperty(ref _CTtime, value); }
}
private bool _TorqueStatus = false;
public bool TorqueStatus
{
get { return _TorqueStatus; }
set { _TorqueStatus = value; }
}
private ObservableCollection _dfc = new ObservableCollection();
///
/// CTQ数据集合
///
public ObservableCollection DFC
{
get { return _dfc; }
set { SetProperty(ref _dfc, value); }
}
private ObservableCollection _ProductList;
public ObservableCollection ProductList
{
get { return _ProductList; }
set { SetProperty(ref _ProductList, value); }
}
#endregion
#region 命令
#endregion
#region 事件
#endregion
public Management(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IConfigService configService,
IRobotService robotService, ICameraService cameraService, IFeederService feederService, IPlcService plcService, IProductService productService,
ICalibrationService calibrationService, IRemoteCommandService remoteCommandService, ISystemDatabaseService systemDatabaseService, ILightManagerService lightManagerService,
IMesService mesService)
{
_regionManager = regionManager;
_eventAggregator = ea;
_container = container;
_configService = configService;
_robotService = robotService;
_cameraService = cameraService;
_feederService = feederService;
_plcService = plcService;
_calibrationService = calibrationService;
yieldtimer = new Timer(DoYieldTime, null, 10000, 1000);
Renders = new ObservableCollection();
_productService = productService;
_remoteCommandService = remoteCommandService;
_systemDatabaseService = systemDatabaseService;
_lightManagerService = lightManagerService;
_mesService = mesService;
_eventAggregator.GetEvent().Subscribe(CurrentRunStatus);
}
#region 读取配置参数
public async Task ReadConfig()
{
await ReadNumber();
}
///
/// 读取数量配置
///
public async Task ReadNumber()
{
try
{
NumStatistics = new NumberStatistics();
var latestHeadChangeRecord = await _systemDatabaseService.GetLatestHeadChangeRecordAsync();
if (latestHeadChangeRecord != null)
{
NumStatistics.HeaderNum = latestHeadChangeRecord.CurrentLockCount;
}
var latestNozzleChangeRecord = await _systemDatabaseService.GetLatestNozzleChangeRecordAsync();
if (latestNozzleChangeRecord != null)
{
NumStatistics.NozzleNum = latestNozzleChangeRecord.CurrentLockCount;
}
NumStatistics.Feeder1Num = _configService.GetRemainingCountByFeederNumber(1);
NumStatistics.Feeder2Num = _configService.GetRemainingCountByFeederNumber(2);
}
catch (Exception ex)
{
NumStatistics = new NumberStatistics();
LogHelper.WriteLogError("加载数量配置文件时出错!", ex);
}
}
#endregion
#region 初始化硬件模块
///
/// 初始化Feeder
///
///
public async Task<(bool IsSucceed, string Msg)> InitFeeders()
{
var feeders = _configService.GetAllFeeders();
StringBuilder sb = new StringBuilder();
foreach (var item in feeders)
{
_feederService.CreateFeeder(item.Id, item.FeederNo, item.FeederName, item.IP, item.Port, item.FeederBrand);
var feeder = _feederService.GetFeeder(item.Id);
Status.Add(new StatusInfo(item.Id, item.FeederName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
try
{
feeder.FeederConnectionStatusChanged += Management_FeederConnectionStatusChanged;
await feeder.ConnectAsync(); //连接Feeder
//SendTaskMessage($"{item.FeederName}已连接!", MessageLevel.Debug);
}
catch (Exception ex)
{
sb.AppendLine($"{item.FeederName}:{Lang.断开连接}![{ex.Message}]");
SendTaskMessage($"{item.FeederName}{Lang.断开连接}!", MessageLevel.Alarm);
}
}
if (sb.Length == 0)
{
return (true, "");
}
else
{
return (false, sb.ToString());
}
}
///
/// 初始化机器人
///
///
public async Task<(bool IsSucceed, string Msg)> InitRobots()
{
var robots = _configService.GetAllRobots();
StringBuilder sb = new StringBuilder();
foreach (var item in robots)
{
_robotService.CreateRobot(item.Id, item);
try
{
Status.Add(new StatusInfo(item.Id, item.RobotName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
var robot = _robotService.GetRobot(item.Id);
robot.ReceivedEvent += Management_ReceivedEvent;
robot.SendEvent += Management_SendEvent;
robot.ConnectedEvent += Management_ConnectedEvent;
robot.DisconnectedEvent += Management_DisconnectedEvent;
await robot.ConnectAsync(); //连接Robot
SendTaskMessage($"{item.RobotName}{Lang.已连接}!", MessageLevel.Debug);
}
catch (Exception ex)
{
sb.AppendLine($"{item.RobotName}:{Lang.断开连接}![{ex.Message}]");
SendTaskMessage($"{item.RobotName}{Lang.断开连接}!", MessageLevel.Alarm);
}
}
if (sb.Length == 0)
{
return (true, "");
}
else
{
return (false, sb.ToString());
}
}
///
/// 初始化PLC
///
///
public async Task<(bool IsSucceed, string Msg)> InitPlc()
{
var plcs = _configService.GetAllPlcs();
StringBuilder sb = new StringBuilder();
foreach (var item in plcs)
{
try
{
_plcService.CreatePlc(item.Id, item);
var plc = _plcService.GetPlc(item.Id);
Status.Add(new StatusInfo(item.Id, item.Name + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
plc.ConnectChangedEvent += Management_PlcConnectChangedEvent;
await plc.ConnectAsync(); //连接PLC
//SendTaskMessage($"{item.Name}{Lang.已连接}!", MessageLevel.Debug);
}
catch (Exception ex)
{
sb.AppendLine($"{item.Name}:{Lang.断开连接}![{ex.Message}]");
SendTaskMessage($"{item.Name}{Lang.断开连接}!", MessageLevel.Alarm);
}
}
try
{
await WriteSystemParameterToPlcAsync(_configService.GetSystemConfiguration());
RegisterPlcMonitorAddress();
}
catch (Exception ex)
{
LogHelper.WriteLogError(Lang.为PLC写入系统参数和订阅变量时出错, ex);
}
if (sb.Length == 0)
{
return (true, "");
}
else
{
return (false, sb.ToString());
}
}
///
/// 初始化相机
///
///
public async Task<(bool IsSucceed, string Msg)> InitCameras()
{
var cameras = _configService.GetAllCameras();
StringBuilder sb = new StringBuilder();
foreach (var item in cameras)
{
try
{
//Renders.Add(new ShowRender() { Id = item.Id, CameraName = item.CameraName });
_cameraService.CreateCamera(item.Id, item);
var camera = _cameraService.GetCamera(item.Id);
Status.Add(new StatusInfo(item.Id, item.CameraName + $"{Lang.未连接}", new SolidColorBrush(Colors.Red)));
camera.CameraConnectChangedEvent += Management_CameraConnectChangedEvent;
try
{
if (await camera.OpenDeviceAsync())
{
SendTaskMessage($"{item.CameraName}{Lang.已连接}!", MessageLevel.Debug);
}
else
{
SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm);
sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!");
}
}
catch (Exception ex)
{
SendTaskMessage($"{item.CameraName}{ex.Message}!", MessageLevel.Alarm);
sb.AppendLine($"{item.CameraName}:{ex.Message}!");
}
}
catch (Exception ex)
{
SendTaskMessage($"{item.CameraName}{Lang.连接失败}!", MessageLevel.Alarm);
sb.AppendLine($"{item.CameraName}:{Lang.连接失败}!:{ex.Message}");
}
}
//_container.Resolve().Renders = Renders;
if (sb.Length == 0)
{
return (true, "");
}
else
{
return (false, sb.ToString());
}
}
///
/// 初始化光源控制器
///
///
public async Task<(bool IsSucceed, string Msg)> InitLightControllers()
{
var lightcontrollers = _configService.GetAllLightControllers();
StringBuilder sb = new StringBuilder();
foreach (var item in lightcontrollers)
{
try
{
var lishtCtr = await _lightManagerService.RegisterControllerAsync(item.Id, item);
//var lightcontroller = _cameraService.GetLightController(item.Id);
//await lightcontroller.ConnectAsync(); //连接光源控制器
//SendTaskMessage($"{item.LightControllerName}{Lang.已连接}!", MessageLevel.Debug);
}
catch (Exception ex)
{
//sb.AppendLine($"{item.LightControllerName}:{Lang.断开连接}![{ex.Message}]");
//SendTaskMessage($"{item.LightControllerName}{Lang.断开连接}!", MessageLevel.Alarm);
}
}
if (lightcontrollers.Count > 0)
{
var result = await _lightManagerService.ConnectAllAsync();
if (result)
{
return (true, "");
}
else
{
return (false, "Light Controller Connect Failed!");
}
}
return (true, "");
//if (sb.Length == 0)
//{
// return (true, "");
//}
//else
//{
// return (false, sb.ToString());
//}
}
///
/// 初始化后台TCP服务器通讯
///
public void InitBgTcp()
{
var config = _configService.GetBgTcp();
try
{
BgCommunicate = new BgCommunicate(config);
BgCommunicate.ConnectedEvent += BgCommunicate_ConnectedEvent;
BgCommunicate.DisconnectedEvent += BgCommunicate_DisconnectedEvent;
BgCommunicate.ReceivedEvent += BgCommunicate_ReceivedEvent;
BgCommunicate.SendEvent += BgCommunicate_SendEvent;
BgCommunicate.StartListening();
SendTaskMessage($"{Lang.已开启监听端口}:{config.Port}", MessageLevel.Debug);
}
catch (Exception ex)
{
SendTaskMessage($"{Lang.未开启监听端口}:{config.Port}", MessageLevel.Error);
LogHelper.WriteLogError(Lang.开启后台服务器通讯时出错, ex);
}
}
///
/// 初始化后台ModbusTCP从站
///
public void InitModbusTcp()
{
try
{
var config = _configService.GetBgModbusTcp();
BgModbusTcpCommunicate = new BgModbusTcpCommunicate(config);
if (config.IsEnabled)
{
BgModbusTcpCommunicate.Listen();
SendTaskMessage($"Start Modbus Tcp Slave:{BgModbusTcpCommunicate.IP}:{BgModbusTcpCommunicate.Port}:{BgModbusTcpCommunicate.SlaveID}", MessageLevel.Debug);
}
}
catch (Exception ex)
{
SendTaskMessage($"No Start Modbus Tcp Slave:{ex.Message}", MessageLevel.Alarm);
LogHelper.WriteLogError(Lang.开启后台ModbusTCP通讯时出错, ex);
}
}
///
/// 初始化后台脚本
///
public void InitBackgroundcript()
{
try
{
if (!Directory.Exists(FilePath.BackgroundScriptPath))
{
Directory.CreateDirectory(FilePath.BackgroundScriptPath);
}
try
{
string[] files = Directory.GetFiles(FilePath.BackgroundScriptPath);
List ScriptFilesCollection = new List();
foreach (string file in files)
{
string fileName = Path.GetFileName(file);
string fileExtension = Path.GetExtension(file);
if (fileExtension == ".cs")
{
ScriptFilesCollection.Add(file);
}
}
foreach (var item in ScriptFilesCollection)
{
try
{
string script = FileHelper.ReadFile(item);
SendTaskMessage($"{Lang.执行脚本}:{item}", MessageLevel.Info);
Task.Factory.StartNew(() =>
{
ScriptHelper.ExecuteScriptingAsync(script, _regionManager, _eventAggregator, _container, rst =>
{
if (!rst.Item1)
{
foreach (var err in rst.Item2)
{
SendTaskMessage($"{Lang.后台脚本出错}:{err}", MessageLevel.Error);
}
}
});
});
}
catch (Exception ex)
{
LogHelper.WriteLogError(string.Format(Lang.开启脚本时出错0, item), ex);
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogError(Lang.读取本地脚本文件列表时出错, ex);
}
}
catch (Exception ex)
{
LogHelper.WriteLogError(Lang.开启后台脚本时出错, ex);
}
}
///
/// 初始化电批
///
///
public async Task InitScrewDriver()
{
try
{
var screwDriverConfig = _configService.GetScrewDriverConfig();
ScrewDriver = new XYD_ScrewDriver(screwDriverConfig.IPAdress, screwDriverConfig.Port);
Status.Add(new StatusInfo(ScrewDriver.Id, Lang.电批未连接, new SolidColorBrush(Colors.Red)));
ScrewDriver.ConnectStateChangedEvent += ScrewDriver_ConnectStateChangedEvent;
ScrewDriver.SendTimeout = screwDriverConfig.SendTimeout;
screwDriverConfig.ReceiveTimeout = screwDriverConfig.ReceiveTimeout;
var isConnected = await ScrewDriver.ConnectAsync();
if (isConnected)
{
SendTaskMessage(Lang.电批已连接, MessageLevel.Debug);
}
else
{
SendTaskMessage(Lang.电批连接失败, MessageLevel.Error);
}
}
catch (Exception ex)
{
SendTaskMessage(string.Format(Lang.电批连接失败0, ex.Message), MessageLevel.Alarm);
LogHelper.WriteLogError(Lang.连接电批时出错, ex);
}
}
///
/// 初始化扭力测量仪
///
///
public async Task InitSignal()
{
var torqueConfig = _configService.GetTorqueReaderConfig();
if (torqueConfig != null)
{
try
{
var port = FileHelper.ReadJsonFile(FilePath.TorqueReaderConfigurationPath);
if (string.IsNullOrEmpty(port.TorqueMeasureSerialPort))
{
client = new TouchSocket.SerialPorts.SerialPortClient();
client.Connecting = (client1, e) => { return EasyTask.CompletedTask; };//即将连接到端口
client.Connected = (client1, e) => { return EasyTask.CompletedTask; };//成功连接到端口
client.Closing = (client1, e) => { return EasyTask.CompletedTask; };//即将从端口断开连接。此处仅主动断开才有效。
client.Closed = (client1, e) => { return EasyTask.CompletedTask; };//从端口断开连接,当连接不成功时不会触发。
client.Received = async (c, e) =>
{
//await Console.Out.WriteLineAsync(Encoding.UTF8.GetString(e.ByteBlock.Buffer));
await Console.Out.WriteLineAsync(e.ByteBlock.Span.ToString(Encoding.ASCII));
};
client.Received = Sp_DataReceived;
await client.SetupAsync(new TouchSocketConfig()
.SetSerialPortOption(new SerialPortOption()
{
BaudRate = torqueConfig.BaudRate,//波特率
DataBits = torqueConfig.DataBits,//数据位
Parity = torqueConfig.Parity,//校验位
PortName = torqueConfig.PortName,//COM
StopBits = torqueConfig.StopBits//停止位
})
.SetSerialDataHandlingAdapter(() => new PeriodPackageAdapter() { CacheTimeout = TimeSpan.FromMilliseconds(100) })
);
await client.ConnectAsync();
}
else
{
SendTaskMessage("请设置扭力测量仪串口!", MessageLevel.Error);
}
}
catch (Exception ex)
{
LogHelper.WriteLogError("初始化扭力测量仪时出错!", ex);
SendTaskMessage("初始化扭力测量仪时出错!" + ex.Message, MessageLevel.Error);
}
}
else
{
TorqueSerialPortConfig config = new TorqueSerialPortConfig();
FileHelper.WriteJsonFile(config, FilePath.TorqueReaderConfigurationPath);
}
}
#endregion
#region 机器人指令执行
public Dictionary RobotTCPCollection = new Dictionary();
///
/// 机器人TCP接收
///
///
///
///
private async void Management_ReceivedEvent(Guid robotid, object arg2, TouchSocket.Sockets.ReceivedDataEventArgs e)
{
try
{
if (!RobotTCPCollection.ContainsKey(robotid))
RobotTCPCollection.Add(robotid, new CancellationTokenSource());
else
{
if (RobotTCPCollection[robotid].IsCancellationRequested)
{
RobotTCPCollection[robotid] = new CancellationTokenSource();
}
}
var robot = await _robotService.GetRobotAsync(robotid);
//从服务器收到信息。但是一般byteBlock和requestInfo会根据适配器呈现不同的值。
var buffer = e.ByteBlock.Span.ToString(robot.GetEncoding());
SendTaskMessage($"{robot.Name}Received:{buffer}", MessageLevel.Info);
var _ = _remoteCommandService.ExecuteAsync(buffer, RobotTCPCollection[robotid].Token, async msg =>
{
try
{
if (robot.ConnectType == TCPConnectType.Client)
{
if (robot.IsConnected)
{
await robot.SendAsync($"{msg}");
}
}
else
{
var cient = (ITcpSessionClient)arg2;
if (cient.Online)
{
await robot.SendAsync(cient, $"{msg}");
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogError(Lang.TCP发送数据时出错, ex);
SendTaskMessage(ex.Message, MessageLevel.Alarm);
}
});
}
catch (Exception ex)
{
LogHelper.WriteLogError(Lang.处理机器人接收的命令时出错, ex);
SendTaskMessage(ex.Message, MessageLevel.Alarm);
}
}
#endregion
#region 机器人&Feeder&相机连接状态事件
///
/// 机器人发送事件
///
///
///
///
///
private void Management_SendEvent(Guid robotid, object arg2, string arg3)
{
var r = _robotService.GetRobot(robotid);
SendTaskMessage($"{r.Name}Send:{arg3}", MessageLevel.Info);
}
///
/// 机器人断开连接时
///
///
///
///
///
private void Management_DisconnectedEvent(Guid robotid, object arg2, ClosedEventArgs arg3)
{
if (RobotTCPCollection.ContainsKey(robotid))
{
RobotTCPCollection[robotid].Cancel();
}
var r = _configService.GetRobot(robotid);
if (r.ConnectType == TCPConnectType.Server)
{
ITcpSessionClient socketClient = arg2 as ITcpSessionClient;
SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.断开连接}!", MessageLevel.Error);
var robot = _robotService.GetRobot(robotid);
if (robot.TcpService.Count < 1)
{
var sta = Status.FirstOrDefault(s => s.ID == robotid);
App.Current.Dispatcher.Invoke(new
Action(() =>
{
sta.Message = $"{r.RobotName}{Lang.未连接}";
sta.Background = new SolidColorBrush(Colors.Red);
}));
}
}
else
{
SendTaskMessage($"{r.RobotName}{Lang.断开连接}!", MessageLevel.Error);
var sta = Status.FirstOrDefault(s => s.ID == robotid);
App.Current.Dispatcher.Invoke(new
Action(() =>
{
sta.Message = $"{r.RobotName}{Lang.断开连接}";
sta.Background = new SolidColorBrush(Colors.Red);
}));
}
}
///
/// 机器人连接成功时
///
///
///
///
///
private void Management_ConnectedEvent(Guid robotid, object arg2, ConnectedEventArgs arg3)
{
var r = _configService.GetRobot(robotid);
if (r.ConnectType == TCPConnectType.Server)
{
ITcpSessionClient socketClient = arg2 as ITcpSessionClient;
SendTaskMessage($"{r.RobotName}[{socketClient.Id}:{socketClient.IP}]{Lang.已连接}!", MessageLevel.Debug);
var robot = _robotService.GetRobot(robotid);
if (robot.TcpService.Count > 0)
{
var sta = Status.FirstOrDefault(s => s.ID == robotid);
App.Current.Dispatcher.Invoke(new
Action(() =>
{
sta.Message = $"{r.RobotName}{Lang.已连接}";
sta.Background = new SolidColorBrush(Colors.Green);
}));
}
}
else
{
SendTaskMessage($"{r.RobotName}{Lang.已连接}!", MessageLevel.Debug);
var sta = Status.FirstOrDefault(s => s.ID == robotid);
App.Current.Dispatcher.Invoke(new
Action(() =>
{
sta.Message = $"{r.RobotName}{Lang.已连接}";
sta.Background = new SolidColorBrush(Colors.Green);
}));
}
}
///
/// Feeder连接状态改变时
///
///
///
private void Management_FeederConnectionStatusChanged(object sender, bool state)
{
IFeeder feeder = sender as IFeeder;
var sta = Status.FirstOrDefault(s => s.ID == feeder.Id);
if (state)
{
App.Current.Dispatcher.Invoke(new
Action(() =>
{
sta.Message = $"{feeder.Name}{Lang.已连接}";
sta.Background = new SolidColorBrush(Colors.Green);
}));
SendTaskMessage($"{feeder.Name}{Lang.已连接}!", MessageLevel.Debug);
}
else
{
App.Current.Dispatcher.Invoke(new
Action(() =>
{
sta.Message = $"{feeder.Name}{Lang.未连接}";
sta.Background = new SolidColorBrush(Colors.Red);
}));
SendTaskMessage($"{feeder.Name}{Lang.断开连接}!", MessageLevel.Error);
}
}
///
/// 相机连接或者断开时
///
///
///
private void Management_CameraConnectChangedEvent(Guid id, bool state)
{
var sta = Status.FirstOrDefault(s => s.ID == id);
var camera = _configService.GetCamera(id);
if (sta != null && camera != null)
{
if (state)
{
App.Current.Dispatcher.Invoke(new Action(() =>
{
sta.Message = $"{camera.CameraName}{Lang.已连接}";
sta.Background = new SolidColorBrush(Colors.Green);
}));
}
else
{
App.Current.Dispatcher.Invoke(new Action(() =>
{
sta.Message = $"{camera.CameraName}{Lang.断开连接}";
sta.Background = new SolidColorBrush(Colors.Red);
}));
}
}
}
///
/// PLC连接状态改变事件
///
///
///
private async void Management_PlcConnectChangedEvent(object sender, bool isconnected)
{
IPlc plc = sender as IPlc;
var sta = Status.FirstOrDefault(s => s.ID == plc.Id);
if (plc.IsConnected)
{
SendTaskMessage($"{plc.Name}{Lang.已连接}!", MessageLevel.Debug);
if (sta == null) return;
App.Current.Dispatcher.Invoke(new
Action(() =>
{
sta.Message = $"{plc.Name}{Lang.已连接}";
sta.Background = new SolidColorBrush(Colors.Green);
}));
try
{
await WriteSystemParameterToPlcAsync(_configService.GetSystemConfiguration());
await WriteParameterToPlcExAsync();
}
catch (Exception ex)
{
LogHelper.WriteLogError("PLC连接成功时,写入系统参数和点位信息时出错!", ex);
}
}
else
{
SendTaskMessage($"{plc.Name}{Lang.断开连接}!", MessageLevel.Error);
if (sta == null) return;
App.Current.Dispatcher.Invoke(new
Action(() =>
{
sta.Message = $"{plc.Name}{Lang.未连接}";
sta.Background = new SolidColorBrush(Colors.Red);
}));
}
}
///
/// 电批连接状态改变时
///
///
///
private void ScrewDriver_ConnectStateChangedEvent(object screwDevice, bool state)
{
try
{
XYD_ScrewDriver screwDriver = screwDevice as XYD_ScrewDriver;
if (state)
{
SendTaskMessage(Lang.电批已连接, MessageLevel.Debug);
var sta = Status.FirstOrDefault(s => s.ID == screwDriver.Id);
App.Current.Dispatcher.Invoke(new
Action(() =>
{
sta.Message = Lang.电批已连接;
sta.Background = new SolidColorBrush(Colors.Green);
}));
}
else
{
SendTaskMessage(Lang.电批未连接, MessageLevel.Error);
var sta = Status.FirstOrDefault(s => s.ID == screwDriver.Id);
App.Current.Dispatcher.Invoke(new
Action(() =>
{
sta.Message = Lang.电批未连接;
sta.Background = new SolidColorBrush(Colors.Red);
}));
}
}
catch (Exception ex)
{
LogHelper.WriteLogError(Lang.电批连接状态改变时出错了, ex);
}
}
#endregion
#region 后台服务器通讯事件
private void BgCommunicate_SendEvent(ITcpSessionClient client, string msg)
{
SendTaskMessage($"{Lang.服务器}[{client.ServicePort}] Send To {Lang.客户端}[{client.Id}:{client.IP}]:{msg}", MessageLevel.Info);
}
public Dictionary BgCommunicateollection = new Dictionary();
private async void BgCommunicate_ReceivedEvent(ITcpSessionClient client, string msg)
{
try
{
if (!BgCommunicateollection.ContainsKey(client.Id))
{
BgCommunicateollection.Add(client.Id, new CancellationTokenSource());
}
SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]ReceiveFor{Lang.客户端}[{client.Id}:{client.IP}]:{msg}", MessageLevel.Info);
var _ = _remoteCommandService.ExecuteAsync(msg, BgCommunicateollection[client.Id].Token, async response =>
{
try
{
if (!client.Online)
{
return;
}
await BgCommunicate.SendAsync(client, $"{response}");
}
catch (Exception ex)
{
LogHelper.WriteLogError(Lang.后台TCP发送数据时出错, ex);
SendTaskMessage(ex.Message, MessageLevel.Alarm);
}
});
}
catch (Exception ex)
{
LogHelper.WriteLogError(Lang.处理后台服务器接收的命令时出错, ex);
SendTaskMessage(ex.Message, MessageLevel.Alarm);
}
}
private void BgCommunicate_DisconnectedEvent(ITcpSessionClient client, ClosedEventArgs e)
{
SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]{Lang.客户端}[{client.Id}:{client.IP}]{Lang.断开连接}!", MessageLevel.Alarm);
if (BgCommunicateollection.ContainsKey(client.Id))
{
BgCommunicateollection[client.Id].Cancel();
}
}
private void BgCommunicate_ConnectedEvent(ITcpSessionClient client, ConnectedEventArgs e)
{
SendTaskMessage($"{Lang.服务器}[{client.ServicePort}]{Lang.客户端}[{client.Id}:{client.IP}]{Lang.连接成功}!", MessageLevel.Debug);
}
///
/// 扭力测量仪接收信息
///
///
///
///
private async Task Sp_DataReceived(ISerialPortClient client, ReceivedDataEventArgs e)
{
try
{
string poname = client.MainSerialPort.PortName;
string data = e.ByteBlock.Span.ToString(Encoding.ASCII);
await Console.Out.WriteLineAsync(data);
string response = data.Split('\r')[0];
SendTaskMessage($"{poname}Receive:{response}", MessageLevel.Info);
SignalValue = "";
SignalValue = response;
}
catch (Exception ex)
{
LogHelper.WriteLogError("扭力测量仪接收信息时出错!", ex);
SendTaskMessage("扭力测量仪接收信息时出错!" + ex.Message, MessageLevel.Error);
Thread.Sleep(50);
}
}
#endregion
#region PLC
///
/// 将系统参数写入PLC
///
///
///
public async Task WriteSystemParameterToPlcAsync(SystemConfiguration systemConfiguration)
{
var addressConfig = _configService.GetPlcAddresses();
var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
if (plc == null || !plc.IsConnected) return;
Dictionary nodeValues = new Dictionary();
nodeValues.Add(addressConfig.Out_WorkMode.Address, (Int16)systemConfiguration.WorkMode);
nodeValues.Add(addressConfig.Out_PickPointNum.Address, (Int16)systemConfiguration.PickPointNum);
nodeValues.Add(addressConfig.Out_DowmCameraNum.Address, (Int16)systemConfiguration.DowmCameraNum);
nodeValues.Add(addressConfig.Out_UpCameraPickNum.Address, (Int16)systemConfiguration.UpCameraPickNum);
nodeValues.Add(addressConfig.Out_UpCameraPutNum.Address, (Int16)systemConfiguration.UpCameraPutNum);
nodeValues.Add(addressConfig.Out_UPCameracheckNum.Address, (Int16)systemConfiguration.UPCameracheckNum);
nodeValues.Add(addressConfig.Out_FixedCameraPickNum.Address, (Int16)systemConfiguration.FixedCameraPickNum);
nodeValues.Add(addressConfig.Out_FixedCameraPutNum.Address, (Int16)systemConfiguration.FixedCameraPutNum);
nodeValues.Add(addressConfig.Out_FixedCameracheckNum.Address, (Int16)systemConfiguration.FixedCameracheckNum);
nodeValues.Add(addressConfig.Out_ScrewTeachNum.Address, (Int16)systemConfiguration.ScrewTeachNum);
var res = await plc.WriteNodesAsync(nodeValues);
}
///
/// 注册需要监控的PLC地址
///
public void RegisterPlcMonitorAddress()
{
var addressConfig = _configService.GetPlcAddresses();
var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
if (plc == null || !plc.IsConnected) return;
//PLC地址数组
List monitorNodeIds = new List()
{
addressConfig.In_DowmCameraExe.Address,
addressConfig.In_UpCameraPickExe.Address,
addressConfig.In_UpCameraPutExe.Address,
addressConfig.In_UPCameracheckExe.Address,
addressConfig.In_FixedCameraPickExe.Address,
addressConfig.In_FixedCameraPutExe.Address,
addressConfig.In_FixedCameracheckExe.Address,
addressConfig.In_WorkStart.Address,
addressConfig.In_WorkDone.Address,
//addressConfig.In_MesCheck.Address,
addressConfig.In_RequestPosition.Address,
addressConfig.In_QrCodePhotoRequest.Address,
addressConfig.In_ScrewTeachExe.Address,
addressConfig.In_Screw_checkExe.Address,
addressConfig.In_PickINC.Address,
addressConfig.In_Throw.Address,
addressConfig.RunMode.Address,
addressConfig.AutoRunning.Address,
addressConfig.ManualMode.Address,
addressConfig.ClearMode.Address,
addressConfig.Fault.Address,
addressConfig.In_FzCameraPixExe.Address,
};
var systemconfig = _configService.GetSystemConfiguration();
for (int i = 0; i < 10; i++)
{
if (systemconfig.DoubleSoftMode && i == 1)
{
continue;
}
monitorNodeIds.Add(string.Format(addressConfig.In_MesCheck.Address, i));
}
//订阅PLC地址变化
//plc.SubscribeNodes("AutoSensor", monitorNodeIds, PlcCommandTrigger, OpcUaSubscriptionMode.CustomPolling);
plc.SubscribeNodes("AutoSensor", monitorNodeIds, PlcCommandTrigger, OpcUaSubscriptionMode.CustomPolling);
// 2. 监听重连事件,重连成功后自动重新订阅
plc.ConnectChangedEvent -= Plc_ConnectChanged; // 防止重复注册
plc.ConnectChangedEvent += Plc_ConnectChanged;
// 定义重连处理方法(写在同一个类里即可)
void Plc_ConnectChanged(object sender, bool isConnected)
{
if (isConnected)
{
// 重连成功后,重新订阅节点
plc.SubscribeNodes("AutoSensor", monitorNodeIds, PlcCommandTrigger, OpcUaSubscriptionMode.CustomPolling);
}
}
}
///
/// 将产品的参数统一写入PLC
///
///
public async Task WriteParameterToPlcExAsync()
{
await WritePositionToPlcAsync();// 写入点位信息给PLC
//await WriteParameterToPlcAsync();// 写入组装上下限给PLC
await WriteProductLoadModeToPLC();//把当前产品型号,写入PLC
}
///
/// 写入点位信息给PLC
///
///
public async Task WritePositionToPlcAsync()
{
var addressConfig = _configService.GetPlcAddresses();
var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
if (plc == null || !plc.IsConnected) return;
var current = _productService.GetCurrentProduct();
if (current == null) return;
_LastAutoRunning = plc.ReadNode(addressConfig.AutoRunning.Address);
if (current.PickCameraPoints != null && current.PickCameraPoints.Count > 0)
{
foreach (var item in current.PickCameraPoints)
{
await item.WriteToPlcAddress(addressConfig.Out_PickCamera, plc);
}
}
if (current.PickPoints != null && current.PickPoints.Count > 0)
{
foreach (var item in current.PickPoints)
{
await item.WriteToPlcAddress(addressConfig.Out_Pick, plc, item.Feeder);
//await item.WriteToPlcAddress(addressConfig.Out_Pick, plc);
}
}
if (current.SecPosCameraPoints != null && current.SecPosCameraPoints.Count > 0)
{
foreach (var item in current.SecPosCameraPoints)
{
await item.WriteToPlcAddress(addressConfig.Out_SecPosCamera, plc);
}
}
if (current.ScrewCameraPoints != null && current.ScrewCameraPoints.Count > 0)
{
int index = 0;
foreach (var item in current.ScrewCameraPoints)
{
//根据使用的点位进行排序
if (item.IsUse)
{
index++;
await item.WriteToPlcAddress(addressConfig.Out_ScrewCamera, plc, index);
}
}
}
if (current.ScrewPoints != null && current.ScrewPoints.Count > 0)
{
int index = 0;
foreach (var item in current.ScrewPoints)
{
if (item.IsUse)
{
index++;
await item.WriteToPlcAddressEx(addressConfig.Out_Screw, plc, index);
}
}
await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)index);
}
if (current.CheckCameraPoints != null && current.CheckCameraPoints.Count > 0)
{
int index = 0;
foreach (var item in current.CheckCameraPoints)
{
if (item.IsUse)
{
index++;
await item.WriteToPlcAddress(addressConfig.Out_CheckCamera, plc, index);
}
}
}
if (current.QrCodePoints != null && current.QrCodePoints.Count > 0)
{
foreach (var item in current.QrCodePoints)
{
await item.WriteToPlcAddress(addressConfig.Out_QrCodeCamera, plc);
}
}
}
///
/// 将当前产品类型写入PLC
///
///
public async Task WriteProductLoadModeToPLC(int modeIndex)
{
var addressConfig = _configService.GetPlcAddresses();
var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
if (plc == null || !plc.IsConnected) return;
await plc.WriteNodeAsync(addressConfig.Out_ProductLoadMode.Address, (Int16)modeIndex);
}
///
/// 将当前产品类型写入PLC
///
///
public async Task WriteProductLoadModeToPLC()
{
var addressConfig = _configService.GetPlcAddresses();
var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
if (plc == null || !plc.IsConnected) return;
var current = _productService.GetCurrentProduct();
if (current == null) return;
var qMes = await _systemDatabaseService.QueryProductFromMesAsync(current.Name);
if (qMes == null) return;
await plc.WriteNodeAsync(addressConfig.Out_ProductLoadMode.Address, (Int16)qMes.ProductLoadMode);
}
PointF[] DowmCameraResults = new PointF[10];
PointF[] UpCameraPutResults = new PointF[10];
bool _LastDowmCameraExe = false;
bool _LastScrewTeachExe = false;
bool _LastUpCameraPickExe = false;
bool _LastScrewFuJianExe = false;
bool _LastUpCameraPutExe = false;
bool _LastUPCameracheckExe = false;
bool _LastFixedCameraPickExe = false;
bool _LastFixedCameraPutExe = false;
bool _LastFixedCameracheckExe = false;
bool _LastFzCameracheckExe = false;
bool _LastThrowExe = false;
bool _LastRunModel = false;
public bool _LastAutoRunning = false;
bool _LastManualMode = false;
bool _LastClearMode = false;
bool _LastFault = false;
Int16 _LastWorkStart = 0;
Int16 _LastWorkDone = 0;
Int16 _LastRequestPosition = 0;
Int16 _LastQrCodePhotoRequest = 0;
Int16[] _LastMesCheck = new Int16[10];
Int16 _LastPickINC = 0;
DateTime[] _LastMesCheckTime = new DateTime[10];
Dictionary mes_cc = new Dictionary();
Dictionary Point = new Dictionary();
string[] ProductMainSN { get; set; } = new string[10];//产品主sn
string[] model_num { get; set; } = new string[10];//产品model_num
string[] wo { get; set; } = new string[10];//产品wo
string[] color { get; set; } = new string[10];//产品color
string[] ppid { get; set; } = new string[10];//产品ppid
string[] revision { get; set; } = new string[10];//产品revision
Dictionary dataToMes = new Dictionary();//上传mes的数据
List resultToMes { get; set; } = new List();//上传mes的数据
///
/// PLC命令触发时
///
///
private async void PlcCommandTrigger((string key, string nodeId, object value) tuple)
{
try
{
if (tuple.key != "AutoSensor")
{
SendTaskMessage("PLC指令错误", MessageLevel.Debug);
return;
}
var addressConfig = _configService.GetPlcAddresses();
var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
if (plc == null || !plc.IsConnected)
{
SendTaskMessage("plc为空", MessageLevel.Debug);
return;
}
// 获取当前产品
var currentProduct = _productService.GetCurrentProduct();
if (currentProduct == null)
{
return;
}
////获取产品编码
//_productService.CurrentProductCode = plc.ReadNode(string.Format(addressConfig.In_Code.Address, 0));
////如果产品编码为空,则以当前的时间戳作为编码
//if (string.IsNullOrEmpty(_productService.CurrentProductCode))
//{
// _productService.CurrentProductCode = DateTime.Now.ToString("yyyyMMddHHmmssfff");
//}
// 触发下相机拍照
if (addressConfig.In_DowmCameraExe.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastDowmCameraExe != state)
{
_LastDowmCameraExe = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage(Lang.触发下相机拍照, MessageLevel.Debug);
//相机的编号
Int16 cameraIndex = plc.ReadNode(addressConfig.In_DowmCameraNum.Address);
SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
// 获取流程
var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId);
if (process == null)
{
SendTaskMessage(string.Format(Lang.固定向上相机1视觉流程流程ID0, currentProduct.FixedUpCamera1ProcedureId), MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
return;
}
// 获取机器人当前坐标
var robot = _robotService.GetAllRobots().FirstOrDefault();
if (robot == null)
{
SendTaskMessage(Lang.未找到机器人无法执行上相机锁附组装拍照视觉任务, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
return;
}
var currentPosition = robot.GetRobotPos();
// 执行视觉任务
var _cts = new CancellationTokenSource();
ImageParameters imgPara = new ImageParameters();
imgPara.SNCode = productSn;
var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token, cameraIndex, cameraIndex, null, imgPara);
if (visionResult.IsSucceed)
{
//披头偏差
float offsetX = (float)(visionResult.X - currentPosition.X);
float offsetY = (float)(visionResult.Y - currentPosition.Y);
DowmCameraResults[cameraIndex] = new PointF(offsetX, offsetY);
await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)1);
return;
}
else
{
await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)2);
return;
}
}
else
{
SendTaskMessage(Lang.触发下相机拍照信号已关闭, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_DownCameraStatus.Address, (Int16)0);
}
}
}
// 触发下相机拍照披头
if (addressConfig.In_ScrewTeachExe.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastScrewTeachExe != state)
{
_LastScrewTeachExe = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage(Lang.触发下相机拍照, MessageLevel.Debug);
//相机的编号
Int16 cameraIndex = plc.ReadNode(addressConfig.In_DowmCameraNum.Address);
SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
// 获取流程
var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCamera1ProcedureId);
if (process == null)
{
SendTaskMessage(string.Format(Lang.固定向上相机1视觉流程流程ID0, currentProduct.FixedUpCamera1ProcedureId), MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2);
return;
}
// 获取机器人当前坐标
var robot = _robotService.GetAllRobots().FirstOrDefault();
if (robot == null)
{
SendTaskMessage(Lang.未找到机器人无法执行上相机锁附组装拍照视觉任务, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2);
return;
}
var currentPosition = robot.GetRobotPos();
// 执行视觉任务
var _cts = new CancellationTokenSource();
ImageParameters imgPara = new ImageParameters();
imgPara.SNCode = productSn;
var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, null, _cts.Token, cameraIndex, cameraIndex, null, imgPara);
if (visionResult.IsSucceed)
{
//披头偏差
float offsetX = (float)(visionResult.X - currentPosition.X);
float offsetY = (float)(visionResult.Y - currentPosition.Y);
DowmCameraResults[cameraIndex] = new PointF(offsetX, offsetY);
await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)1);
return;
}
else
{
await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)2);
return;
}
}
else
{
SendTaskMessage(Lang.触发下相机拍照信号已关闭, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_ScrewTeachOk.Address, (Int16)0);
}
}
}
// 触发上相机取料拍照
else if (addressConfig.In_UpCameraPickExe.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastUpCameraPickExe != state)
{
_LastUpCameraPickExe = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage(Lang.触发上相机取料拍照, MessageLevel.Debug);
//相机的编号
//Int16 cameraIndex = plc.ReadNode(addressConfig.In_UpCameraPickNum.Address);
await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)1);
}
else
{
SendTaskMessage(Lang.触发上相机取料拍照信号已关闭, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_UpCameraPickStatus.Address, (Int16)0);
}
}
}
// 上相机锁附/组装拍照
else if (addressConfig.In_UpCameraPutExe.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastUpCameraPutExe != state)
{
_LastUpCameraPutExe = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage(Lang.触发上相机锁附组装拍照, MessageLevel.Debug);
//相机的编号
Int16 cameraIndex = plc.ReadNode(addressConfig.In_UpCameraPutNum.Address);
SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
//视觉流程ID
Guid procedureId = currentProduct.MoveDownCameraLockPhotoProcedureId;
List plcPoints = new List();
foreach (var item in currentProduct.ScrewCameraPoints)
{
if (item.IsUse)
{
plcPoints.Add(item);
}
}
int num = plcPoints.Count;
//获取锁付拍照的点位
var pos = plcPoints[cameraIndex - 1];
if (pos != null)
{
procedureId = pos.VisionProcessID;
}
// 获取流程
var process = _productService.GetCurrentProductProcedureModelById(procedureId);
if (process == null)
{
SendTaskMessage(string.Format(Lang.未找到上相机锁附组装拍照对应的视觉流程流程ID0, procedureId), MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
return;
}
// 获取机器人当前坐标
var robot = _robotService.GetAllRobots().FirstOrDefault();
if (robot == null)
{
SendTaskMessage(Lang.未找到机器人无法执行上相机锁附组装拍照视觉任务, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
return;
}
var currentPosition = robot.GetRobotPos();
var systemConfig = _configService.GetSystemConfiguration();
// 执行视觉任务
var _cts = new CancellationTokenSource();
ImageParameters imgPara = new ImageParameters();
imgPara.SNCode = productSn;
var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, num, null, imgPara);
if (visionResult.IsSucceed)
{
//是拍一个锁一个还是拍两个点锁所有
if (systemConfig.IsBasePartOpenLightKB)
{
if (mes_cc.ContainsKey(visionResult.distance.Split(',')[0]))
{
mes_cc[visionResult.distance.Split(',')[0]] = visionResult.distance.Split(',')[1];
}
else
{
mes_cc.Add(visionResult.distance.Split(',')[0], visionResult.distance.Split(',')[1]);
}
}
//拍1打1
if (systemConfig.WorkMode == 1)
{
UpCameraPutResults[1] = new PointF((float)visionResult.X, (float)visionResult.Y);
}
else if (systemConfig.WorkMode == 2)
{
UpCameraPutResults[cameraIndex] = new PointF((float)visionResult.X, (float)visionResult.Y);
}
else if (systemConfig.WorkMode == 3)
{
if (cameraIndex <= 3)
{
UpCameraPutResults[1] = new PointF((float)visionResult.X, (float)visionResult.Y);
}
else
{
UpCameraPutResults[cameraIndex] = new PointF((float)visionResult.X, (float)visionResult.Y);
}
}
if (resultToMes.Contains($"Screw{cameraIndex}_Fail"))
{
resultToMes.Remove($"Screw{cameraIndex}_Fail");
SendTaskMessage($"Screw{cameraIndex}:重复拍照成功", MessageLevel.Debug);
}
await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)1);
}
else
{
if (systemConfig.IsBasePartOpenLightKB)
{
if (mes_cc.ContainsKey(visionResult.distance.Split(',')[0]))
{
mes_cc[visionResult.distance.Split(',')[0]] = visionResult.distance.Split(',')[1];
}
else
{
if (mes_cc.ContainsKey(cameraIndex.ToString()))
{
mes_cc[cameraIndex.ToString()] = "0";
}
else
{
mes_cc.Add(cameraIndex.ToString(), "0");
}
}
}
if (!resultToMes.Contains($"Screw{cameraIndex}_Fail"))
{
resultToMes.Add($"Screw{cameraIndex}_Fail");
SendTaskMessage($"Screw{cameraIndex}:拍照失败", MessageLevel.Error);
}
await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
}
}
else
{
SendTaskMessage(Lang.触发上相机锁附组装拍照信号已关闭, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)0);
}
}
}
// 上相机检测拍照
else if (addressConfig.In_UPCameracheckExe.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastUPCameracheckExe != state)
{
_LastUPCameracheckExe = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage(Lang.触发上相机检测拍照, MessageLevel.Debug);
//相机的编号
Int16 cameraIndex = plc.ReadNode(addressConfig.In_UPCameracheckNum.Address);
SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
//获取锁付拍照的点位
List plcPoints = new List();
foreach (var item in currentProduct.CheckCameraPoints)
{
if (item.IsUse)
{
plcPoints.Add(item);
}
}
int num = plcPoints.Count;
//视觉流程ID
Guid procedureId = Guid.Empty;
//获取锁付拍照的点位
var pos = plcPoints[cameraIndex - 1];
if (pos != null)
{
procedureId = pos.VisionProcessID;
}
// 获取流程
var process = _productService.GetCurrentProductProcedureModelById(procedureId);
if (process == null)
{
SendTaskMessage(string.Format("未找到点位:{0}的视觉流程:{1}", cameraIndex, procedureId), MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)2);
return;
}
// 获取机器人当前坐标
var robot = _robotService.GetAllRobots().FirstOrDefault();
if (robot == null)
{
SendTaskMessage("未找到机器人无法执行 上相机检测拍照视觉任务", MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_UpCameraPutStatus.Address, (Int16)2);
return;
}
var currentPosition = robot.GetRobotPos();
// 执行视觉任务
var _cts = new CancellationTokenSource();
var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, num, null, null);
if (visionResult.IsSucceed)
{
await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)1);
SendTaskMessage($"点位{cameraIndex}检测OK", MessageLevel.Debug);
}
else
{
await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)2);
SendTaskMessage($"点位{cameraIndex}检测NG", MessageLevel.Error);
}
}
else
{
SendTaskMessage(Lang.触发上相机检测拍照信号已关闭, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)0);
}
}
}
//下相机拍照螺丝复检
else if (addressConfig.In_Screw_checkExe.Address.Contains(tuple.nodeId))
{
Point.Clear();
if (tuple.value is bool state)
{
if (_LastScrewFuJianExe != state)
{
_LastScrewFuJianExe = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage(Lang.触发相机拍照螺丝复检, MessageLevel.Debug);
//相机的编号
Int16 cameraIndex = plc.ReadNode(addressConfig.In_DowmCameraNum.Address);
SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
// 获取流程
var process = _productService.GetCurrentProductProcedureModelById(currentProduct.FixedUpCameraProcedureId);
if (process == null)
{
SendTaskMessage(string.Format(Lang.固定向上相机视觉流程流程ID, currentProduct.FixedUpCameraProcedureId), MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_Screw_check_status.Address, (Int16)2);
return;
}
// 获取机器人当前坐标
var robot = _robotService.GetAllRobots().FirstOrDefault();
if (robot == null)
{
SendTaskMessage(Lang.未找到机器人无法执行上相机锁附组装拍照视觉任务, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_Screw_check_status.Address, (Int16)2);
return;
}
var currentPosition = robot.GetRobotPos();
// 执行视觉任务
var _cts = new CancellationTokenSource();
ImageParameters imgPara = new ImageParameters();
imgPara.SNCode = productSn;
var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint1(process, null, null, _cts.Token, cameraIndex, cameraIndex, null, imgPara);
//string X, Y, U;
if (visionResult.IsSucceed)
{
//披头偏差
//float offsetX = (float)(visionResult.X - currentPosition.X);
//float offsetY = (float)(visionResult.Y - currentPosition.Y);
string[] keys = { "X", "Y", "U" };
double[] values = { visionResult.X, visionResult.Y, visionResult.U };
for (int i = 0; i < keys.Length; i++)
{
Point.Add(keys[i], values[i]);
}
// 2. 生成CSV文件(替换成你的保存路径)
string csvPath = @"D:\视觉检测数据\XYU结果.csv";
// 调用生成方法(appendMode=true 每次拍照新增一行,不会覆盖历史数据)
ExportDictionaryToCsv(Point, csvPath, appendMode: true);
//DowmCameraResults[cameraIndex] = new PointF(offsetX, offsetY);
//ExportDictionaryToCsv1(ProductMainSN[1], visionResult.X, visionResult.Y, visionResult.U);
await plc.WriteNodeAsync(addressConfig.Out_Screw_check_status.Address, (Int16)1);
return;
}
else
{
await plc.WriteNodeAsync(addressConfig.Out_Screw_check_status.Address, (Int16)2);
return;
}
}
else
{
SendTaskMessage(Lang.触发下相机拍照信号已关闭, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_Screw_check_status.Address, (Int16)0);
}
}
}
// 固定相机取料拍照
else if (addressConfig.In_FixedCameraPickExe.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastFixedCameraPickExe != state)
{
_LastFixedCameraPickExe = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage(Lang.触发固定相机取料拍照, MessageLevel.Debug);
//相机的编号
//Int16 cameraIndex = plc.ReadNode(addressConfig.In_FixedCameraPickNum.Address);
await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)1);
}
else
{
SendTaskMessage(Lang.触发固定相机取料拍照信号已关闭, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPickStatus.Address, (Int16)0);
}
}
}
// 固定相机锁附/组装拍照
else if (addressConfig.In_FixedCameraPutExe.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastFixedCameraPutExe != state)
{
_LastFixedCameraPutExe = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage(Lang.触发固定相机锁附组装拍照, MessageLevel.Debug);
//相机的编号
//Int16 cameraIndex = plc.ReadNode(addressConfig.In_FixedCameraPutNum.Address);
await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)1);
}
else
{
SendTaskMessage(Lang.触发固定相机锁附组装拍照信号已关闭, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_FixedCameraPutStatus.Address, (Int16)0);
}
}
}
// 固定相机检测拍照
else if (addressConfig.In_FixedCameracheckExe.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastFixedCameracheckExe != state)
{
_LastFixedCameracheckExe = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage(Lang.触发固定相机检测拍照, MessageLevel.Debug);
//相机的编号
//Int16 cameraIndex = plc.ReadNode(addressConfig.In_FixedCameracheckNum.Address);
await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)1);
}
else
{
SendTaskMessage(Lang.触发固定相机检测拍照信号已关闭, MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_FixedCameracheckStatus.Address, (Int16)0);
}
}
}
// 翻转相机检测拍照
else if (addressConfig.In_FzCameraPixExe.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastFzCameracheckExe != state)
{
_LastFzCameracheckExe = state;
}
else
{
return;
}
if (state)
{
int i = 5;
SendTaskMessage("触发翻转相机拍照", MessageLevel.Debug);
var code = plc.ReadNode(string.Format(addressConfig.In_Code.Address, 1));
string resultphoto = "";
//这里缺少从MES获取产品配方的内容,需要补充
_LastMesCheckTime[5] = (await _mesService.GetServerTimeAsync(5, code, 2)).Now;
var stationConfig2 = _configService.GetDeviceInfo(0);
LogHelper.WriteLogMes($"【mes开始QUERY】");
SendTaskMessage($"mes开始QUERY", MessageLevel.Info);
//if (stationConfig2.EnableMES)
//{
// (bool isSuccess, string response) = await _mesService.StationGetExAsync(code, "", 2);
// if (isSuccess)
// {
// string ProductMainSN_Temp = "";
// LogHelper.WriteLogMes($"询问mes成功");
// SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
// string[] item = response.Split('\n');
// //获取过度件sn
// string snItem = item.FirstOrDefault(f => f.Contains("sn="));
// if (snItem != null)
// {
// ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
// SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
// }
// LogHelper.WriteLogMes($"询问mes成功");
// SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
// //20260214,螺丝机特别版
// (bool isSuccess2, string response2) = await _mesService.StationGetAsync(ProductMainSN_Temp, "", 2);//询问mes
// if (isSuccess2)
// {
// LogHelper.WriteLogMes($"询问mes成功");
// SendTaskMessage($"询问mes成功:{response2}", MessageLevel.Debug);
// string[] items = response2.Split('\n');
// //获取产品主sn和wo用于后续上传数据使用
// string snItem2 = items.FirstOrDefault(f => f.Contains("sn="));
// if (snItem2 != null)
// {
// var codesn = snItem2.Replace("sn=", "").Trim();
// //_productService.CurrentProductCode = codesn;
// SendTaskMessage($"产品主sn:{codesn}", MessageLevel.Info);
// }
// string woItem = item.FirstOrDefault(f => f.Contains("wo="));
// if (woItem != null)
// {
// wo[i] = woItem.Replace("wo=", "").Trim();
// SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
// }
// string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
// if (modelItem != null)
// {
// model_num[i] = modelItem.Replace("model_num=", "").Trim();
// SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
// }
// string colorItem = item.FirstOrDefault(f => f.Contains("color="));
// if (colorItem != null)
// {
// color[i] = colorItem.Replace("color=", "").Trim();
// SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
// }
// string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
// if (ppidItem != null)
// {
// ppid[5] = ppidItem.Replace("ppid=", "").Trim();
// SendTaskMessage($"ppid:{ppid[5]}", MessageLevel.Info);
// }
// string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
// if (revisionItem != null)
// {
// revision[5] = revisionItem.Replace("stage=", "").Trim();
// SendTaskMessage($"stage:{revision[5]}", MessageLevel.Info);
// }
// LogHelper.WriteLogMes($"第二次询问mes成功");
// SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
// }
// else
// {
// LogHelper.WriteLogMes($"第二次询问mes失败");
// SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error);
// await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)2);
// return;
// }
// }
// else
// {
// LogHelper.WriteLogMes($"询问mes失败");
// SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
// await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)2);
// return;
// }
//}
//else
//{
// SendTaskMessage($"mes未启用", MessageLevel.Info);
//}
Guid FzCameraProcedureId = currentProduct.FzCameraProcedureId;
var FzCameraProcess = _productService.GetCurrentProductProcedureModelById(FzCameraProcedureId);
if (FzCameraProcess == null)
{
SendTaskMessage($"未找到翻转相机视觉流程,流程ID: {FzCameraProcedureId}", MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)2);
return;
}
SendTaskMessage($"开始执行翻转相机视觉流程", MessageLevel.Debug);
var _cts = new CancellationTokenSource();
await _remoteCommandService.SetLightBeforePhoto(FzCameraProcess);
var FzCameraResult = _remoteCommandService.ExecutePhotoGetDistance(FzCameraProcess, null);
if (FzCameraResult)
{
SendTaskMessage("翻转相机拍照成功", MessageLevel.Debug);
resultphoto = "Pass";
await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)1);
}
else
{
SendTaskMessage("翻转相机拍照失败", MessageLevel.Alarm);
resultphoto = "Fail";
await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)2);
}
if (stationConfig2.SaveCsv)
{
List DFC = new List();
string saveppid = "NA";
string names = currentProduct.Name;
if (stationConfig2.SaveCsvPPID) saveppid = ppid[i];
DateTime servertime = DateTime.Now;
// Dictionary adjacentDiffDict1 = GetAdjacentDiffDict(keyData1);
//判断锁付路径字符串是否为空,如果不为空则保存,否则不保存
if (!string.IsNullOrEmpty(stationConfig2.SaveCsvPath))
{
DFC.Add(new DataFormCtq
{
PPID = saveppid,
PROCESS_NAME = stationConfig2.DataInfo.PROCESS_NAME,
PROCESS_START_TIME = _LastMesCheckTime[5].ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
CTQ_NAME = "FzCameraPhoto",
CTQ_VALUE = 0,
CTQ_LSL = 0,
CTQ_USL = 0,
PROCESS_END_TIME = servertime.ToString("yyyy-MM-ddTHH:mm:ss.fffffff") + "+08:00",
SUPPLIER_NAME = stationConfig2.DataInfo.SUPPLIER_NAME,
COMMODITY_TYPE = stationConfig2.DataInfo.COMMODITY_TYPE,
REVISION = stationConfig2.DataInfo.REVISION + "_" + revision[i],
WO = "wo",//
MFG_LOT = stationConfig2.DataInfo.MFG_LOT,//
MFG_ASSY_LINE = stationConfig2.DataInfo.MFG_ASSY_LINE,
PROCESS_OUTCOME = resultphoto,
PROCESS_MACHINE_ID = stationConfig2.DataInfo.PROCESS_MACHINE_ID,
CTQ_UNIT_OF_MEASURE = "mm",
ERROR_MESSAGE = "",
PD_ATTR_01 = _productService.CurrentProductCode,//
PD_ATTR_02 = "",
PD_ATTR_03 = "",
PD_ATTR_04 = code,
PD_ATTR_05 = "",
});
bool recordResult = RecordStationResult(0, DFC, _productService.CurrentProductCode, servertime.Subtract(_LastMesCheckTime[5]).TotalSeconds, _LastMesCheckTime[5], stationConfig2.SaveCsvPath);
if (recordResult)
{
SendTaskMessage($"翻转相机拍照产品记录保存成功", MessageLevel.Debug);
}
else
{
SendTaskMessage($"{stationConfig2.DeviceName}产品记录保存失败", MessageLevel.Error);
}
}
else
{
SendTaskMessage($"{stationConfig2.DeviceName}工位未配置保存路径,无法保存产品记录", MessageLevel.Error);
}
}
}
else
{
SendTaskMessage($"触发翻转相机检测拍照信号已关闭", MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_FzCameraPickStatus.Address, (Int16)0);
}
}
}
// 开始锁付
else if (addressConfig.In_WorkStart.Address.Contains(tuple.nodeId))
{
if (tuple.value is Int16 state)
{
if (_LastWorkStart != state)
{
_LastWorkStart = state;
}
else
{
return;
}
var code = plc.ReadNode(string.Format(addressConfig.In_Code.Address, 0));
if (state == 1)
{
SendTaskMessage(Lang.收到开始锁付信号, MessageLevel.Debug);
StartScrewTime = DateTime.Now;
//位置编号
Int16 positionIndex = plc.ReadNode(addressConfig.In_ProductNum.Address);
SendTaskMessage(string.Format(Lang.编号0, positionIndex), MessageLevel.Info);
// 开始采集压力值,ProductMainSN[i]
//StartPressureCollection(currentProduct.Name, _productService.CurrentProductCode, 0);
StartPressureCollection(currentProduct.Name, _productService.CurrentProductCode, 0);
}
else
{
SendTaskMessage(Lang.开始锁付信号已关闭, MessageLevel.Alarm);
// 关闭时的处理(如需)可放在这里
}
}
}
// 停止锁付
else if (addressConfig.In_WorkDone.Address.Contains(tuple.nodeId))
{
if (tuple.value is Int16 state)
{
if (_LastWorkDone != state)
{
_LastWorkDone = state;
}
else
{
return;
}
if (state == 1 || state == 2)
{
if (TorqueStatus)
{
}
SendTaskMessage(Lang.收到停止锁付信号, MessageLevel.Debug);
//位置编号
Int16 positionIndex = plc.ReadNode(addressConfig.In_ProductNum.Address);
SendTaskMessage(string.Format(Lang.编号0, positionIndex), MessageLevel.Info);
//获取当前锁付点
var screwPoint = currentProduct.ScrewPoints.FirstOrDefault(p => p.Number == positionIndex);
//获取当前的机器人点位
var robot = _robotService.GetAllRobots().FirstOrDefault();
var currentPosition = robot.GetRobotPos();
//读取压力值
float pressure = _maxPressure;
//当前用户名
string currentUserName = _systemDatabaseService.GetCurrentUser().UserName;
//吸嘴最后一次的记录
var nozzleRecord = await _systemDatabaseService.GetLatestNozzleChangeRecordAsync();
//最后一次的吸嘴编号
string nozzleNumber = nozzleRecord != null ? nozzleRecord.NozzleBatchNumber : "N/A";
//披头最后一次的记录
var headRecord = await _systemDatabaseService.GetLatestHeadChangeRecordAsync();
//最后一次的披头编号
string headNumber = headRecord != null ? headRecord.HeadBatchNumber : "N/A";
await Task.Delay(60);
//获取电批结果
bool isSucced = ScrewDriver.GetData();
LockResult lockResult = new LockResult();
lockResult.ScrewdriverGetDataSucceed = isSucced;
lockResult.Number = positionIndex;
lockResult.LockPassed = ScrewDriver.Success;
lockResult.ProductName = currentProduct.Name;
lockResult.ProductNumber = _productService.CurrentProductCode;
lockResult.LockTurns = (float)ScrewDriver.Truns;
lockResult.LockTorque = (float)ScrewDriver.TorqueValue;
lockResult.LockDuration = (float)ScrewDriver.FinalTime;
lockResult.ScrewdriverProgramNumber = ScrewDriver.ProgramNumber;
lockResult.ResultDetails = ScrewDriver.ErrorInfo;
lockResult.Pressure = pressure;
lockResult.TargetTorqueUpperLimit = ScrewDriver.TargetTorqueUpperLimit;
lockResult.TargetTorqueLowerLimit = ScrewDriver.TargetTorqueLowerLimit;
lockResult.TargetTurnsUpperLimit = ScrewDriver.TargetTurnsUpperLimit / 360;
lockResult.TargetTurnsLowerLimit = ScrewDriver.TargetTurnsLowerLimit / 360;
lockResult.TargetLockSpeed = ScrewDriver.TargetLockSpeed;
lockResult.PositionX = currentPosition.X;
lockResult.PositionY = currentPosition.Y;
lockResult.StartPositionZ = screwPoint.Z_Position_Start;
lockResult.EndPositionZ = currentPosition.Z;
lockResult.LockDescentSpeed = screwPoint.Z_Velocity_Stop;
lockResult.FeederNumber = screwPoint != null ? screwPoint.Feeder : 0;
lockResult.NozzleNumber = nozzleNumber;
lockResult.HeadNumber = headNumber;
lockResult.OperatorName = currentUserName;
lockResult.ProductImagePath = "";
lockResult.WaveDatas = ScrewDriver.WaveDatas;
//pressure = 0;
//获取系统参数
var systemConfig = _configService.GetSystemConfiguration();
//判断是否保存锁付曲线
if (systemConfig.IsSaveScrewCurve)
{
//锁付曲线的保存路径为:systemConfig.ScrewCurvePath//年月//年月日//配方名//产品SN//产品SN+螺丝索引
string datePath = DateTime.Now.ToString("yyyy-MM") + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\";
string dirPath = System.IO.Path.Combine(systemConfig.ScrewCurvePath, datePath, currentProduct.Name, lockResult.ProductNumber);
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
string curveFileName = $"{lockResult.ProductNumber}-{lockResult.Number}.png";
//锁付CSV保存路径
string curveFileFullPath = System.IO.Path.Combine(dirPath, $"{lockResult.ProductNumber}-{lockResult.Number}.csv");
//锁付曲线保存路径
lockResult.LockCurveImagePath = System.IO.Path.Combine(dirPath, curveFileName);
var _ = lockResult.SaveToCsvAsync(curveFileFullPath);
}
if (lockResult.Number == 1)
{
dataToMes.Clear();
resultToMes.Clear();
lockResultsAll.Clear();
}
//基于当前的锁付结果和产品的规格参数判断锁付结果是否合格
var stationConfig = _configService.GetDeviceInfo(0);
bool torqueRes = false, turnRes = false, pressureRes = false;
bool lastRes = false;//默认不合格,只有当扭力、圈数、压力都在设定的上下限范围内时,才判定为锁付合格
if (lockResult.LockTorque >= lockResult.TargetTorqueLowerLimit && lockResult.LockTorque <= lockResult.TargetTorqueUpperLimit)
{
torqueRes = true;
}
if (lockResult.Pressure >= stationConfig.DataInfo.CTQ_LSL_Pressure && lockResult.Pressure <= stationConfig.DataInfo.CTQ_USL_Pressure)
{
pressureRes = true;
}
if (lockResult.LockTurns >= lockResult.TargetTurnsLowerLimit && lockResult.LockTurns <= lockResult.TargetTurnsUpperLimit)
{
turnRes = true;
}
if (torqueRes == true && turnRes == true && pressureRes == true)//当扭力、圈数、压力都在设定的上下限范围内时,才判定为锁付合格
{
lastRes = true;
}
if (dataToMes.Keys.Contains($"Torque{lockResult.Number}"))
{
dataToMes.Remove($"Torque{lockResult.Number}");
}
if (dataToMes.Keys.Contains($"Turn{lockResult.Number}"))
{
dataToMes.Remove($"Turn{lockResult.Number}");
}
if (dataToMes.Keys.Contains($"Pressure{lockResult.Number}"))
{
dataToMes.Remove($"Pressure{lockResult.Number}");
}
lockResult.LockPassed = lastRes;
dataToMes.Add($"Torque{lockResult.Number}", $"{lockResult.LockTorque:F2}");
dataToMes.Add($"Turn{lockResult.Number}", $"{lockResult.LockTurns:F2}");
dataToMes.Add($"Pressure{lockResult.Number}", $"{lockResult.Pressure:F2}");
//dataToMes.Add($"LastResult{lockResult.Number}", $"{lastRes}");
if (!lastRes)//如果不合格,则在MES结果中添加"Screw{lockResult.Number}_Fail"
{
if (!resultToMes.Contains($"Screw{lockResult.Number}_Fail"))
{
resultToMes.Add($"Screw{lockResult.Number}_Fail");
SendTaskMessage($"Screw{lockResult.Number}:锁附不合格", MessageLevel.Error);
}
}
SendTaskMessage($"螺丝序号:{lockResult.Number},扭力:{lockResult.LockTorque},压力:{lockResult.Pressure},圈数:{lockResult.LockTurns}", MessageLevel.Info);
_maxPressure = 0;
lockResultsAll.Add(lockResult);
App.Current.Dispatcher.Invoke(() =>
{
_eventAggregator.GetEvent().Publish(lockResult);
});
//记录锁付结果至数据库
await _systemDatabaseService.RecordLockResultAsync(lockResult);
//披头数量+1
NumStatistics.HeaderNum = await _systemDatabaseService.IncrementLatestHeadLockCountAsync(currentUserName);
//吸嘴数量+1
NumStatistics.NozzleNum = await _systemDatabaseService.IncrementLatestNozzleLockCountAsync(currentUserName);
await plc.WriteNodeAsync(addressConfig.In_WorkDone.Address, (Int16)0);
// 停止采集压力并保存
await StopAndSavePressureCollectionAsync(currentProduct.Name, _productService.CurrentProductCode, positionIndex);
pressure = 0;
}
else
{
SendTaskMessage(Lang.停止锁付信号已关闭, MessageLevel.Alarm);
}
}
}
// 多相机坐标合并
else if (addressConfig.In_RequestPosition.Address.Contains(tuple.nodeId))
{
if (tuple.value is Int16 state)
{
if (_LastRequestPosition != state)
{
_LastRequestPosition = state;
}
else
{
return;
}
if (state == 1)
{
SendTaskMessage(Lang.收到多相机坐标合并请求开始合并, MessageLevel.Debug);
//位置编号
Int16 positionIndex = plc.ReadNode(addressConfig.In_ProductNum.Address);
//是拍一个锁一个还是拍两个点锁所有
var systemConfig = _configService.GetSystemConfiguration();
//拍1打1
if (systemConfig.WorkMode == 1)
{
List plcPoints = new List();
foreach (var item in currentProduct.ScrewPoints)
{
if (item.IsUse)
{
plcPoints.Add(item);
}
}
var TempPoint = plcPoints[positionIndex - 1];
if (TempPoint == null)
{
SendTaskMessage($"点:{positionIndex}不存在", MessageLevel.Info);
await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
return;
}
var PositionPoint = TempPoint.Clone();
PositionPoint.X_Position = UpCameraPutResults[1].X + DowmCameraResults[1].X;
PositionPoint.Y_Position = UpCameraPutResults[1].Y + DowmCameraResults[1].Y;
SendTaskMessage($"点:{positionIndex},{PositionPoint.X_Position:F3},{PositionPoint.Y_Position:F4}", MessageLevel.Info);
if (PositionPoint.X_Position == 0 || PositionPoint.Y_Position == 0)
{
SendTaskMessage("拟合位置为0,请检查", MessageLevel.Error);
await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
return;
}
if (_configService.GetDeviceInfo().EnableXY)
{
//验证当前锁附点位与设置锁附点位的差值是否超限,超限则报警并停止流程
//目前验证已OK 缺少切换按钮
try
{
var ScrewLimit = currentProduct.GlobalParamListCollection.FirstOrDefault((p) => p.Number == positionIndex - 1);
//判断实际偏差与设置的点位差数是否偏差过大
float XOffsetPoint = Math.Abs(PositionPoint.X_Position - (TempPoint.X_Position + TempPoint.X_Offset));
float YOffsetPoint = Math.Abs(PositionPoint.Y_Position - (TempPoint.Y_Position + TempPoint.Y_Offset));
if (!(XOffsetPoint >= float.Parse(ScrewLimit.Param1) && XOffsetPoint <= float.Parse(ScrewLimit.Param2)) ||
!(YOffsetPoint >= float.Parse(ScrewLimit.Param3) && YOffsetPoint <= float.Parse(ScrewLimit.Param4)))
{
SendTaskMessage($"当前锁附点位{positionIndex}与设置锁附点位差值超限,X偏差:{XOffsetPoint},Y偏差{YOffsetPoint}", MessageLevel.Error);
await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
return;
}
}
catch (Exception ex)
{
SendTaskMessage($"当前锁附点位与设置锁附点位差值未设置:" + ex.Message, MessageLevel.Error);
await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
return;
}
}
await PositionPoint.WriteToPlcAddress(addressConfig.Out_Screw, plc, positionIndex);
}
//拍2打所有
else if (systemConfig.WorkMode == 2)
{
//根据UpCameraPutResults[]中的索引1、2,和拍照点对应的Local下的坐标,从创建Local坐标系
PointF worldP1 = new PointF(UpCameraPutResults[1].X, UpCameraPutResults[1].Y);
PointF worldP2 = new PointF(UpCameraPutResults[2].X, UpCameraPutResults[2].Y);
PointF localP1 = new PointF(currentProduct.ScrewCameraLocalPos1.X_Position, currentProduct.ScrewCameraLocalPos1.Y_Position);
PointF localP2 = new PointF(currentProduct.ScrewCameraLocalPos2.X_Position, currentProduct.ScrewCameraLocalPos2.Y_Position);
CoordinateTransformer local = new CoordinateTransformer(worldP1, worldP2, localP1, localP2);
for (int i = 0; i < currentProduct.ScrewPoints.Count; i++)
{
var localPoint = new PointF(currentProduct.ScrewPoints[i].X_Position + currentProduct.ScrewPoints[i].X_Offset,
currentProduct.ScrewPoints[i].Y_Position + currentProduct.ScrewPoints[i].Y_Offset);
var worldPoint = local.ToOldCoord(localPoint.X, localPoint.Y);
var point = currentProduct.ScrewPoints[i].Clone();
//锁付点位在Local中已经加了偏移,这里写回PLC时需要将偏移清零,否则写入PLC端时会重复计算偏移
point.X_Offset = 0;
point.Y_Offset = 0;
point.X_Position = (float)worldPoint[0];
point.Y_Position = (float)worldPoint[1];
SendTaskMessage($"点:{i},{point.X_Position:F3},{point.Y_Position:F4}", MessageLevel.Info);
if (_configService.GetDeviceInfo().ScrewOverrun)
{
try
{
var ScrewLimit = currentProduct.GlobalParamListCollection.FirstOrDefault((p) => p.Number == positionIndex - 1);
//判断实际偏差与设置的点位差数是否偏差过大
float XOffsetPoint = Math.Abs(localPoint.X - (float)worldPoint[0]);
float YOffsetPoint = Math.Abs(localPoint.Y - (float)worldPoint[1]);
if ((XOffsetPoint >= float.Parse(ScrewLimit.Param1) && XOffsetPoint <= float.Parse(ScrewLimit.Param2)) ||
(YOffsetPoint >= float.Parse(ScrewLimit.Param3) && YOffsetPoint <= float.Parse(ScrewLimit.Param4)))
{
SendTaskMessage($"当前锁附点位{i}与设置锁附点位差值超限,X偏差:{XOffsetPoint},Y偏差{YOffsetPoint}", MessageLevel.Error);
await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
return;
}
}
catch (Exception ex)
{
SendTaskMessage($"当前锁附点位与设置锁附点位差值超限:" + ex.Message, MessageLevel.Error);
await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
}
}
await point.WriteToPlcAddress(addressConfig.Out_Screw, plc);
}
await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)currentProduct.ScrewPoints.Count);
}
//拍2打所有
else if (systemConfig.WorkMode == 3)
{
if (positionIndex <= 3)
{
List plcPoints = new List();
foreach (var item in currentProduct.ScrewPoints)
{
if (item.IsUse)
{
plcPoints.Add(item);
}
}
var point = plcPoints[positionIndex - 1];
if (point == null)
{
SendTaskMessage($"点:{positionIndex}不存在", MessageLevel.Info);
await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
return;
}
point = point.Clone();
point.X_Position = UpCameraPutResults[1].X + DowmCameraResults[1].X;
point.Y_Position = UpCameraPutResults[1].Y + DowmCameraResults[1].Y;
SendTaskMessage($"点:{positionIndex},{point.X_Position:F3},{point.Y_Position:F4}", MessageLevel.Info);
await point.WriteToPlcAddress(addressConfig.Out_Screw, plc, positionIndex);
}
else
{
//根据UpCameraPutResults[]中的索引1、2,和拍照点对应的Local下的坐标,从创建Local坐标系
PointF worldP1 = new PointF(UpCameraPutResults[4].X, UpCameraPutResults[4].Y);
PointF worldP2 = new PointF(UpCameraPutResults[5].X, UpCameraPutResults[5].Y);
PointF localP1 = new PointF(currentProduct.ScrewCameraLocalPos1.X_Position, currentProduct.ScrewCameraLocalPos1.Y_Position);
PointF localP2 = new PointF(currentProduct.ScrewCameraLocalPos2.X_Position, currentProduct.ScrewCameraLocalPos2.Y_Position);
CoordinateTransformer local = new CoordinateTransformer(worldP1, worldP2, localP1, localP2);
for (int i = 0; i < currentProduct.ScrewPoints.Count; i++)
{
var localPoint = new PointF(currentProduct.ScrewPoints[i].X_Position + currentProduct.ScrewPoints[i].X_Offset,
currentProduct.ScrewPoints[i].Y_Position + currentProduct.ScrewPoints[i].Y_Offset);
var worldPoint = local.ToOldCoord(localPoint.X, localPoint.Y);
var point = currentProduct.ScrewPoints[i].Clone();
//锁付点位在Local中已经加了偏移,这里写回PLC时需要将偏移清零,否则写入PLC端时会重复计算偏移
point.X_Offset = 0;
point.Y_Offset = 0;
point.X_Position = (float)worldPoint[0];
point.Y_Position = (float)worldPoint[1];
SendTaskMessage($"点:{i},{point.X_Position:F3},{point.Y_Position:F4}", MessageLevel.Info);
await point.WriteToPlcAddress(addressConfig.Out_Screw, plc);
}
await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)currentProduct.ScrewPoints.Count);
}
}
await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)1);
}
else
{
SendTaskMessage(string.Format(Lang.多相机坐标合并请求已结束或被取消state0, state), MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)0);
}
}
}
// PLC请求二维码拍照位置
else if (addressConfig.In_QrCodePhotoRequest.Address.Contains(tuple.nodeId))
{
if (tuple.value is Int16 state)
{
if (_LastQrCodePhotoRequest != state)
{
_LastQrCodePhotoRequest = state;
}
else
{
return;
}
if (state == 1)
{
SendTaskMessage("收到PLC二维码拍照位置请求", MessageLevel.Debug);
if (currentProduct.QrCodePoints == null || currentProduct.QrCodePoints.Count == 0)
{
SendTaskMessage("未找到示教后的二维码拍照点位", MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
return;
}
var qrpoint = currentProduct.QrCodePoints[0];
//发送点位
bool SendSuccess = await qrpoint.WriteToPlcAddress(addressConfig.Out_QrCodeCamera, plc);
if (SendSuccess)
{
SendTaskMessage("二维码拍照点位发送成功", MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)1);
Guid qrCodeProcedureId = currentProduct.QrCodeProcedureId;
if (qrCodeProcedureId == Guid.Empty)
{
// 如果没有配置二维码视觉流程,直接返回成功
SendTaskMessage("未配置二维码视觉流程,跳过扫码", MessageLevel.Info);
return;
}
}
else
{
SendTaskMessage("二维码拍照点位发送失败", MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
}
}
else
{
SendTaskMessage("二维码拍照点位发送信号已关闭", MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)0);
}
}
}
// 螺丝供料器螺丝递减
else if (addressConfig.In_PickINC.Address.Contains(tuple.nodeId))
{
if (tuple.value is Int16 state)
{
if (_LastPickINC != state)
{
_LastPickINC = state;
}
else
{
return;
}
if (state != 0)
{
SendTaskMessage($"螺丝供料器...", MessageLevel.Info);
await plc.WriteNodeAsync(addressConfig.In_PickINC.Address, (Int16)0);
var feeders = _configService.GetRemainingCountByFeederNumber(state);
if (state == 1)
{
NumStatistics.Feeder1Num = _configService.DecrementScrewCountByFeederNumber(feeders);
}
else if (state == 2)
{
NumStatistics.Feeder2Num = _configService.DecrementScrewCountByFeederNumber(feeders);
}
}
}
}
// 抛料计数
else if (addressConfig.In_Throw.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastThrowExe != state)
{
_LastThrowExe = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage("收到抛料信号", MessageLevel.Info);
await _systemDatabaseService.RecordThrowNumberAsync(new ThrowRecord()
{
DayTime = DateTime.Now,
ThrowCount = 1
});
}
}
}
// 切换成自动运行模式
else if (addressConfig.AutoRunning.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastAutoRunning != state)
{
_LastAutoRunning = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage("收到切换成自动模式", MessageLevel.Info);
}
else
{
SendTaskMessage("收到关闭自动模式", MessageLevel.Info);
}
CurrentRunStatus();
}
}
// 切换成维修模式
else if (addressConfig.ManualMode.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastManualMode != state)
{
_LastManualMode = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage("收到切换成维修模式", MessageLevel.Info);
}
else
{
SendTaskMessage("收到退出维修模式", MessageLevel.Info);
}
CurrentRunStatus();
}
}
// 切换成流线模式
else if (addressConfig.ClearMode.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastClearMode != state)
{
_LastClearMode = state;
}
else
{
return;
}
if (state)
{
SendTaskMessage("收到切换成流线模式", MessageLevel.Info);
}
else
{
SendTaskMessage("收到退出流线模式", MessageLevel.Info);
}
CurrentRunStatus();
}
}
//plc报警
else if (addressConfig.Fault.Address.Contains(tuple.nodeId))
{
if (tuple.value is bool state)
{
if (_LastFault != state)
{
_LastFault = state;
}
else
{
return;
}
if (state)
{
SendAlarmToUi(plc);
SendTaskMessage("收到PLC报警", MessageLevel.Error);
}
else
{
SendTaskMessage("收到PLC消除报警", MessageLevel.Debug);
}
}
}
// 过站检查
else
{
for (int i = 0; i < 10; i++)
{
if (string.Format(addressConfig.In_MesCheck.Address, i).Contains(tuple.nodeId))
{
if (tuple.value is Int16 state)
{
//var oldState = _LastMesCheck[i];
//_LastMesCheck[i] = state;
//SendTaskMessage($"站点{i},收到PLC状态为:{state},旧状态为:{oldState}",MessageLevel.Info);
//if (oldState == state)
//{
// return;
//}
if (_LastMesCheck[i] != state)
{
_LastMesCheck[i] = state;
}
else
{
return;
}
var systemConfig = _configService.GetSystemConfiguration();
string code = "";
//plc启用的模式
bool models = plc.ReadNode(addressConfig.Cam_code_mode.Address);
if (models && state == 1)
{
//拍照读码
SendTaskMessage(Lang.触发上相机锁附组装拍照, MessageLevel.Debug);
//相机的编号
Int16 cameraIndex = plc.ReadNode(addressConfig.In_UpCameraPutNum.Address);
SendTaskMessage(string.Format(Lang.编号0, cameraIndex), MessageLevel.Info);
Guid qrCodeProcedureId = currentProduct.QrCodeProcedureId;
//视觉流程ID
var qrCodeProcess = _productService.GetCurrentProductProcedureModelById(qrCodeProcedureId);
if (qrCodeProcess == null)
{
SendTaskMessage($"未找到二维码视觉流程,流程ID: {qrCodeProcedureId}", MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
return;
}
SendTaskMessage($"开始执行二维码扫码视觉流程", MessageLevel.Debug);
var _cts = new CancellationTokenSource();
await _remoteCommandService.SetLightBeforePhoto(qrCodeProcess);
var qrCodeResult = _remoteCommandService.ExecutePhoto(i + 1, qrCodeProcess, out CogToolBlockTerminalCollection outputCollection);
if (qrCodeResult && outputCollection.Contains("QR"))
{
code = outputCollection["QR"].Value as string;
code = code.ToUpper();
if (!string.IsNullOrEmpty(code) && code.Length > 5)
{
if (systemConfig.IsBasePartCodeChooseScrew)//是否基于零件码,判断是不是当站要打的螺丝
{
var val = CheckCode(code, systemConfig.ChooseScrewModel);
if (val)
{
SendTaskMessage($"是当站螺丝:{code}", MessageLevel.Info);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)1);
}
else
{
SendTaskMessage($"不是当站螺丝:{code}", MessageLevel.Info);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
return;
}
}
bool _isSuccess = plc.WriteNode(string.Format(addressConfig.In_Code.Address, 0), code);
if (_isSuccess)
{
SendTaskMessage($"二维码扫码成功:{code}", MessageLevel.Info);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)1);
}
else
{
SendTaskMessage($"二维码扫码成功:{code}", MessageLevel.Info);
SendTaskMessage($"发送plc二维码失败:{code}", MessageLevel.Error);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
}
}
else
{
SendTaskMessage("二维码扫码失败", MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
return;
}
}
else
{
code = "";
SendTaskMessage("二维码扫码失败", MessageLevel.Alarm);
await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)2);
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
return;
}
}
else
{
code = plc.ReadNode(string.Format(addressConfig.In_Code.Address, i));
}
if (!(i == 0 || i == 7))
{
return;
}
productSn = code;
SendTaskMessage($"收到plc信号{state}", MessageLevel.Debug);
//询问mes
if (state == 1)
{
SendTaskMessage($"收到{i}过站检查请求(当站是否生产)...", MessageLevel.Debug);
//获取过站时间
_LastMesCheckTime[i] = (await _mesService.GetServerTimeAsync(i, code, 2)).Now;
if (_configService.GetDeviceInfo().EnableMES)
{
LogHelper.WriteLogMes($"【mes开始QUERY】");
SendTaskMessage($"mes开始QUERY", MessageLevel.Info);
//dataToMes.Clear();
if (systemConfig.IsBasePartCodeGetSN)//是否基于零件码,判断是否过站
{
SendTaskMessage($"基于零件码,判断是否过站", MessageLevel.Debug);
(bool isSuccess, string response) = await _mesService.StationGetExAsync(code, "", 2);//基于电池的SN获取产品SN,询问mes
if (isSuccess)
{
string ProductMainSN_Temp = "";
LogHelper.WriteLogMes($"询问mes成功");
SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
string[] item = response.Split('\n');
//获取过度件sn
string snItem = item.FirstOrDefault(f => f.Contains("sn="));
if (snItem != null)
{
ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
}
LogHelper.WriteLogMes($"询问mes成功");
SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
//20260214,螺丝机特别版
(bool isSuccess2, string response2) = await _mesService.StationGetAsync(ProductMainSN_Temp, "", 2);//询问mes
if (isSuccess2)
{
LogHelper.WriteLogMes($"询问mes成功");
SendTaskMessage($"询问mes成功:{response2}", MessageLevel.Debug);
item = response2.Split('\n');
snItem = item.FirstOrDefault(f => f.Contains("sn="));
if (snItem != null)
{
ProductMainSN[i] = snItem.Replace("sn=", "").Trim();
if (i == 0)
{
_productService.CurrentProductCode = ProductMainSN[i];
}
//_eventAggregator.GetEvent().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
}
string woItem = item.FirstOrDefault(f => f.Contains("wo="));
if (woItem != null)
{
wo[i] = woItem.Replace("wo=", "").Trim();
SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
}
string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
if (modelItem != null)
{
model_num[i] = modelItem.Replace("model_num=", "").Trim();
SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
}
string colorItem = item.FirstOrDefault(f => f.Contains("color="));
if (colorItem != null)
{
color[i] = colorItem.Replace("color=", "").Trim();
SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
}
string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
if (ppidItem != null)
{
ppid[i] = ppidItem.Replace("ppid=", "").Trim();
SendTaskMessage($"ppid:{ppid[i]}", MessageLevel.Info);
}
string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
if (revisionItem != null)
{
revision[i] = revisionItem.Replace("stage=", "").Trim();
SendTaskMessage($"stage:{revision[i]}", MessageLevel.Info);
}
LogHelper.WriteLogMes($"第二次询问mes成功");
SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
//切换产品
if (i == 0)//i=0时才会切换产品
{
var q = await _systemDatabaseService.QueryProductFromMesAsync(model_num[i], color[i]);
if (q != null && !string.IsNullOrEmpty(q.ProductName))
{
ProductList = new ObservableCollection(_productService.GetAllProducts());
Guid productId = Guid.Empty;
foreach (var itt in ProductList)
{
if (itt.Name == q.ProductName)
{
productId = itt.ID;
break;
}
}
if (currentProduct.ID != productId)
{
//切换产品
_productService.LoadProduct(productId);
_productService.SetCurrentProduct(productId);
await WriteParameterToPlcExAsync();
SendTaskMessage($"切换产品成功", MessageLevel.Debug);
}
}
}
//产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
}
else
{
LogHelper.WriteLogMes($"第二次询问mes失败");
SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error);
//产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
}
}
else
{
LogHelper.WriteLogMes($"询问mes失败");
SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
//产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
}
}
else //直接基于RFID询问是否过站
{
SendTaskMessage($"基于RFID询问是否过站", MessageLevel.Debug);
(bool isSuccess, string response) = await _mesService.StationGetExAsync(code, "", 2);
if (isSuccess)
{
string ProductMainSN_Temp = "";
LogHelper.WriteLogMes($"询问mes成功");
SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
string[] item = response.Split('\n');
//获取过度件sn
string snItem = item.FirstOrDefault(f => f.Contains("sn="));
if (snItem != null)
{
ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
}
LogHelper.WriteLogMes($"询问mes成功");
SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
(bool isSuccess2, string response2) = await _mesService.StationGetAsync(ProductMainSN_Temp, "", 2);//询问mes
if (isSuccess2)
{
item = response2.Split('\n');
snItem = item.FirstOrDefault(f => f.Contains("sn="));
if (snItem != null)
{
ProductMainSN[i] = snItem.Replace("sn=", "").Trim();
if (i == 0)
{
_productService.CurrentProductCode = ProductMainSN[i];
}
SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
}
string woItem = item.FirstOrDefault(f => f.Contains("wo="));
if (woItem != null)
{
wo[i] = woItem.Replace("wo=", "").Trim();
SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
}
string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
if (modelItem != null)
{
model_num[i] = modelItem.Replace("model_num=", "").Trim();
SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
}
string colorItem = item.FirstOrDefault(f => f.Contains("color="));
if (colorItem != null)
{
color[i] = colorItem.Replace("color=", "").Trim();
SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
}
string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
if (ppidItem != null)
{
ppid[i] = ppidItem.Replace("ppid=", "").Trim();
SendTaskMessage($"ppid:{ppid[i]}", MessageLevel.Info);
}
string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
if (revisionItem != null)
{
revision[i] = revisionItem.Replace("stage=", "").Trim();
SendTaskMessage($"stage:{revision[i]}", MessageLevel.Info);
}
LogHelper.WriteLogMes($"第二次询问mes成功");
SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
//切换产品
if (i == 0)//i=0时才会切换产品
{
var q = await _systemDatabaseService.QueryProductFromMesAsync(model_num[i], color[i]);
if (q != null && !string.IsNullOrEmpty(q.ProductName))
{
ProductList = new ObservableCollection(_productService.GetAllProducts());
Guid productId = Guid.Empty;
foreach (var itt in ProductList)
{
if (itt.Name == q.ProductName)
{
productId = itt.ID;
break;
}
}
if (currentProduct.ID != productId)
{
//切换产品
_productService.LoadProduct(productId);
_productService.SetCurrentProduct(productId);
await WriteParameterToPlcExAsync();
SendTaskMessage($"切换产品成功", MessageLevel.Debug);
}
}
}
//产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
return;
}
else
{
LogHelper.WriteLogMes($"第二次询问mes失败");
SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error);
//产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
if (models)
{
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
SendTaskMessage($"发送plc信号3", MessageLevel.Debug);
return;
}
else
{
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
SendTaskMessage($"发送plc信号2", MessageLevel.Debug);
return;
}
}
}
else
{
LogHelper.WriteLogMes($"询问mes失败");
SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
//产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
if (models)
{
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
SendTaskMessage($"发送plc信号3", MessageLevel.Debug);
return;
}
else
{
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
SendTaskMessage($"发送plc信号2", MessageLevel.Debug);
return;
}
}
}
LogHelper.WriteLogMes($"【mes结束QUERY】");
SendTaskMessage($"mes结束QUERY", MessageLevel.Info);
}
else
{
ProductMainSN[i] = DateTime.Now.ToString("yyyyMMddHHmmssfff");
if (i == 0)
{
_productService.CurrentProductCode = ProductMainSN[i];
}
LogHelper.WriteLogMes($"【未启用mes】");
SendTaskMessage($"未启用mes", MessageLevel.Info);
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
SendTaskMessage($"发送plc信号2", MessageLevel.Debug);
}
}
// 上传本站数据
else if (state == 2)
{
DFC.Clear();//CTQ数据集合
// string productccode = ProductMainSN[i];
//获取当前工位的配置
var stationConfig = _configService.GetDeviceInfo(i);
var sysConfig = _configService.GetSystemConfiguration();
SendTaskMessage($"收到{i}:{stationConfig.DeviceName}上传本站数据请求...", MessageLevel.Debug);
//从服务器中获取当前时间
LogHelper.WriteLogMes($"【从服务器中获取当前时间】");
SendTaskMessage($"从服务器中获取当前时间", MessageLevel.Info);
var serverTimeResult = await _mesService.GetServerTimeAsync(i, ProductMainSN[i], 2);
//这里缺少上传MES的本站数据结果及开始和结束时间,需要补充
if (i == 0)
{
CTtime = serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds;
_eventAggregator.GetEvent().Publish(true);//通知界面延迟保存图片,等待MES查询结果返回,避免图片和数据不同步
}
//读取产品结果
Int16 resultIndex = plc.ReadNode(string.Format(addressConfig.In_Result.Address, i));
if (stationConfig.EnableMES)
{
//目前只记录当前站
if (i == 0)
await _systemDatabaseService.RecordProductionAsync(new ProductionRecord()
{
ProductName = currentProduct.Name,
ProductCode = ProductMainSN[i],
DeviceId = i,
DeviceName = "",
Category = resultIndex == 1 ? "OK" : "NG",
Quantity = 1,
Remark = $"TotalSeconds:{serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds.ToString("F2")}",
});
}
LogHelper.WriteLogMes($"【mes开始ADD】");
SendTaskMessage($"mes开始ADD", MessageLevel.Info);
if (i == 0)
{
foreach (var lockResult in lockResultsAll)
{
bool torqueRes, turnRes, pressureRes;
if (lockResult.LockTorque >= lockResult.TargetTorqueLowerLimit && lockResult.LockTorque <= lockResult.TargetTorqueUpperLimit)
{
torqueRes = true;
}
else torqueRes = false;
if (lockResult.Pressure >= stationConfig.DataInfo.CTQ_LSL_Pressure && lockResult.Pressure <= stationConfig.DataInfo.CTQ_USL_Pressure)
{
pressureRes = true;
}
else pressureRes = false;
if (lockResult.LockTurns >= lockResult.TargetTurnsLowerLimit && lockResult.LockTurns <= lockResult.TargetTurnsUpperLimit)
{
turnRes = true;
}
else turnRes = false;
string midValue = "NA", saveppid = "NA";
if (stationConfig.SaveCsvPPID) saveppid = ppid[i];
midValue = ((lockResult.TargetTorqueLowerLimit + lockResult.TargetTorqueUpperLimit) / 2).ToString("F3");
DFC.Add(new DataFormCtq
{
PPID = saveppid,
ScrewNum = lockResult.Number,
PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
CTQ_NAME = "Torque",
CTQ_VALUE = lockResult.LockTorque,
CTQ_LSL = lockResult.TargetTorqueLowerLimit,
CTQ_USL = lockResult.TargetTorqueUpperLimit,
PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
WO = wo[i],
MFG_LOT = stationConfig.DataInfo.MFG_LOT,
MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
PROCESS_OUTCOME = torqueRes ? "PASS" : "FAIL",
PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
CTQ_UNIT_OF_MEASURE = "kgf.cm",
ERROR_MESSAGE = torqueRes ? "NA" : "Torque_Spec OOS",
PD_ATTR_01 = ProductMainSN[i],
PD_ATTR_02 = midValue,
PD_ATTR_03 = "NA",
PD_ATTR_04 = productSn,
PD_ATTR_05 = "NA",
});
midValue = ((lockResult.TargetTurnsLowerLimit + lockResult.TargetTurnsUpperLimit) / 2).ToString("F3");
DFC.Add(new DataFormCtq
{
PPID = saveppid,
ScrewNum = lockResult.Number,
PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
CTQ_NAME = "Turn",
CTQ_VALUE = lockResult.LockTurns,
CTQ_LSL = lockResult.TargetTurnsLowerLimit,
CTQ_USL = lockResult.TargetTurnsUpperLimit,
PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
WO = wo[i],
MFG_LOT = stationConfig.DataInfo.MFG_LOT,
MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
PROCESS_OUTCOME = turnRes ? "PASS" : "FAIL",
PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
CTQ_UNIT_OF_MEASURE = "Turn",
ERROR_MESSAGE = turnRes ? "NA" : "Turn_Spec OOS",
PD_ATTR_01 = ProductMainSN[i],
PD_ATTR_02 = midValue,
PD_ATTR_03 = "NA",
PD_ATTR_04 = productSn,
PD_ATTR_05 = "NA",
});
midValue = ((stationConfig.DataInfo.CTQ_LSL_Pressure + stationConfig.DataInfo.CTQ_USL_Pressure) / 2).ToString("F3");
DFC.Add(new DataFormCtq
{
PPID = saveppid,
ScrewNum = lockResult.Number,
PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
CTQ_NAME = "Pressure",
CTQ_VALUE = lockResult.Pressure,
CTQ_LSL = stationConfig.DataInfo.CTQ_LSL_Pressure,
CTQ_USL = stationConfig.DataInfo.CTQ_USL_Pressure,
PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
WO = wo[i],
MFG_LOT = stationConfig.DataInfo.MFG_LOT,
MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
PROCESS_OUTCOME = pressureRes ? "PASS" : "FAIL",
PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
CTQ_UNIT_OF_MEASURE = "kg",
ERROR_MESSAGE = pressureRes ? "NA" : "Pressure_Spec OOS",
PD_ATTR_01 = ProductMainSN[i],
PD_ATTR_02 = midValue,
PD_ATTR_03 = "NA",
PD_ATTR_04 = productSn,
PD_ATTR_05 = "NA",
});
}
if (systemConfig.IsBasePartOpenLightKB)
{
string midValue = "999", saveppid = "NA";
double upvlalue = 0, doenvlaue = 0;
if (stationConfig.SaveCsvPPID) saveppid = ppid[i];
bool pressureRes = true;
foreach (var item in mes_cc)
{
switch (item.Key)
{
case "1":
upvlalue = Convert.ToDouble(currentProduct.GlobalParamEx.CC1DownLimit.ToString("F3"));
doenvlaue = Convert.ToDouble(currentProduct.GlobalParamEx.CC1UpLimit.ToString("F3"));
midValue = ((currentProduct.GlobalParamEx.CC1DownLimit + currentProduct.GlobalParamEx.CC1UpLimit) / 2).ToString("F3");
if (Convert.ToDouble(item.Value) < upvlalue || Convert.ToDouble(item.Value) > doenvlaue)
{
resultToMes.Add($"CC{item.Key}_Fail");
SendTaskMessage($"CC{item.Key}:超限", MessageLevel.Error);
}
break;
case "2":
upvlalue = Convert.ToDouble(currentProduct.GlobalParamEx.CC2DownLimit.ToString("F3"));
doenvlaue = Convert.ToDouble(currentProduct.GlobalParamEx.CC2UpLimit.ToString("F3"));
midValue = ((currentProduct.GlobalParamEx.CC2DownLimit + currentProduct.GlobalParamEx.CC2UpLimit) / 2).ToString("F3");
if (Convert.ToDouble(item.Value) < upvlalue || Convert.ToDouble(item.Value) > doenvlaue)
{
resultToMes.Add($"CC{item.Key}_Fail");
SendTaskMessage($"CC{item.Key}:超限", MessageLevel.Error);
}
break;
}
dataToMes.Add($"CC{item.Key}", $"{item.Value}");
DFC.Add(new DataFormCtq
{
PPID = saveppid,
ScrewNum = 1,
PROCESS_NAME = stationConfig.DataInfo.PROCESS_NAME,
PROCESS_START_TIME = _LastMesCheckTime[i].ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
CTQ_NAME = $"CC{item.Key}",
CTQ_VALUE = Convert.ToDouble(item.Value.ToString()),
CTQ_LSL = doenvlaue,
CTQ_USL = upvlalue,
PROCESS_END_TIME = serverTimeResult.Now.ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00",
SUPPLIER_NAME = stationConfig.DataInfo.SUPPLIER_NAME,
COMMODITY_TYPE = stationConfig.DataInfo.COMMODITY_TYPE,
REVISION = stationConfig.DataInfo.REVISION + "_" + revision[i],
WO = wo[i],
MFG_LOT = stationConfig.DataInfo.MFG_LOT,
MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
PROCESS_OUTCOME = pressureRes ? "PASS" : "FAIL",
PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
CTQ_UNIT_OF_MEASURE = "mm",
ERROR_MESSAGE = pressureRes ? "NA" : $"CC{item.Key} OOS",
PD_ATTR_01 = ProductMainSN[i],
PD_ATTR_02 = midValue,
PD_ATTR_03 = "NA",
PD_ATTR_04 = productSn,
PD_ATTR_05 = "NA",
});
}
}
}
//上传mes
if (stationConfig.EnableMES)
{
//测试使用,强制上传mes为pass
if (sysConfig.IsMesResult)
{
resultToMes.Clear();
}
(bool isSuccess, string response) = await _mesService.SubmitGetExAsync(ProductMainSN[i], "", dataToMes, resultToMes, _LastMesCheckTime[i], serverTimeResult.Now);
if (isSuccess)
{
LogHelper.WriteLogMes($"上传mes成功");
SendTaskMessage($"上传mes成功", MessageLevel.Debug);
}
else
{
LogHelper.WriteLogMes($"上传mes失败");
SendTaskMessage($"上传mes失败", MessageLevel.Error);
}
}
else
{
LogHelper.WriteLogMes($"上传mes成功");
SendTaskMessage($"上传mes成功", MessageLevel.Debug);
}
LogHelper.WriteLogMes($"【mes结束ADD】");
SendTaskMessage($"mes结束ADD", MessageLevel.Info);
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
if (stationConfig.SaveCsv)
{
if (!string.IsNullOrEmpty(stationConfig.SaveCsvPath))
{
bool recordResult = false;
recordResult = RecordStationResult(i, DFC, ProductMainSN[i], serverTimeResult.Now.Subtract(_LastMesCheckTime[i]).TotalSeconds, _LastMesCheckTime[i], stationConfig.SaveCsvPath);
if (recordResult)
{
SendTaskMessage($"{i}:{stationConfig.DeviceName}产品记录保存成功", MessageLevel.Debug);
//上传数据
try
{
if (stationConfig.EnableMES)
{
DateTime dt = DateTime.Now;
string filename = $"{ProductMainSN[i]}_{dt.ToString("yyyyMMddHHmmss")}";
var device = _configService.GetDeviceInfo();
var config = _configService.GetSystemConfiguration();
(bool isSuc, string resp) = await _mesService.SendFtpFileAsync(ProductMainSN[i], filename);
if (isSuc)
{
LogHelper.WriteLogMes($"上传数据成功");
SendTaskMessage($"上传数据成功", MessageLevel.Debug);
string filePath = $"D:\\image\\Recored\\{dt.ToString("yyyy-MM")}\\{dt.ToString("MM-dd")}\\{currentProduct.Name}\\{ProductMainSN[i]}";
//string source_csv2 = $"{stationConfig.SaveCsvPath}\\{dt.ToString("yyyy-MM")}\\{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}";
if (!Directory.Exists(filePath))
{
Directory.CreateDirectory(filePath);
}
string csvfilename = $"{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}";
//复制csv
string source_csv = $"{stationConfig.SaveCsvPath}\\{dt.ToString("yyyy-MM")}\\{i}_{stationConfig.DeviceName}_{dt.ToString("yyyy-MM-dd")}.csv";
string target_csv = Path.Combine(filePath, Path.GetFileName(source_csv));
File.Copy(source_csv, target_csv, true);
//复制log
string source_log = $"..\\Log\\LogInfo\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}.txt";
string target_log = Path.Combine(filePath, Path.GetFileName(source_log));
File.Copy(source_log, target_log, true);
//判断是否保存锁付曲线
if (systemConfig.IsSaveScrewCurve)
{
string target_Directory = filePath + "\\扭力曲线\\";
string source_Torque_Image = config.ScrewCurvePath + $"\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}\\{currentProduct.Name}\\{ProductMainSN[i]}";
try
{
var col = Directory.GetFiles(source_Torque_Image);
Directory.CreateDirectory(target_Directory);
foreach (var item in col)
{
string target_Torque = target_Directory + item.Replace(source_Torque_Image, "");
File.Copy(item, target_Torque, true);
}
}
catch (Exception)
{
SendTaskMessage($"扭力曲线图读取失败", MessageLevel.Error);
}
}
CompressionImage(filePath, filename, device.Station, device.FixtureId);
//上传csv
bool isAfter20 = dt.Hour >= 20;
//判断是否是新的一天
bool isNewDay = dt.Date != _lastCompressDate.Date;
//当天还没执行过压缩
if (isAfter20 && isNewDay && !_hasCompressedToday)
{
// 执行压缩
CompressionSingleCsv(source_csv, csvfilename, device.Station, device.FixtureId);
_lastCompressDate = dt;
_hasCompressedToday = true;
LogHelper.WriteLogMes("csv上传数据成功");
SendTaskMessage($"csv上传数据成功", MessageLevel.Debug);
}
// 跨天后重置当天执行标记
if (!isNewDay && _hasCompressedToday)
{
_hasCompressedToday = false;
}
}
else
{
LogHelper.WriteLogMes($"上传数据失败");
SendTaskMessage($"上传数据失败", MessageLevel.Error);
}
}
else
{
LogHelper.WriteLogMes($"未启用mes");
SendTaskMessage($"未启用mes", MessageLevel.Error);
}
}
catch (Exception ex)
{
SendTaskMessage($"上传数据时出错:{ex.Message}", MessageLevel.Error);
LogHelper.WriteLogError($"上传数据时出错", ex);
}
}
else
{
SendTaskMessage($"{i}:{stationConfig.DeviceName}产品记录保存失败", MessageLevel.Error);
}
}
else
{
SendTaskMessage($"{i}:{stationConfig.DeviceName}工位未配置保存路径,无法保存产品记录", MessageLevel.Error);
}
}
else
{
SendTaskMessage($"{i}:{stationConfig.DeviceName}工位未配置保存产品记录", MessageLevel.Debug);
}
WriteProductLog(lockResultsAll, ProductMainSN[i], CTtime);
mes_cc.Clear();
}
//kb 螺丝站位询问mes 具体需求跟plc对接
else if (state == 5)
{
SendTaskMessage($"收到KB Screw站点{i}过站检查请求(当站是否生产)...", MessageLevel.Debug);
//获取过站时间
_LastMesCheckTime[i] = (await _mesService.GetServerTimeAsync(i, code, 2)).Now;
if (_configService.GetDeviceInfo().EnableMES)
{
LogHelper.WriteLogMes($"【mes开始QUERY】");
SendTaskMessage($"mes开始QUERY", MessageLevel.Info);
SendTaskMessage($"基于RFID询问是否过站", MessageLevel.Debug);
(bool isSuccess, string response) = await _mesService.StationGetExAsync(code, "", 2);
//如果成功的话就代表跟正常mes询问一样,当前站需工作
if (isSuccess)
{
string ProductMainSN_Temp = "";
LogHelper.WriteLogMes($"询问mes成功");
SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
string[] item = response.Split('\n');
//获取过度件sn
string snItem = item.FirstOrDefault(f => f.Contains("sn="));
if (snItem != null)
{
ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
}
LogHelper.WriteLogMes($"询问mes成功");
SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
(bool isSuccess2, string response2) = await _mesService.StationGetAsync(ProductMainSN_Temp, "", 2);//询问mes
if (isSuccess2)
{
item = response2.Split('\n');
snItem = item.FirstOrDefault(f => f.Contains("sn="));
if (snItem != null)
{
ProductMainSN[i] = snItem.Replace("sn=", "").Trim();
if (i == 0)
{
_productService.CurrentProductCode = ProductMainSN[i];
}
SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
}
string woItem = item.FirstOrDefault(f => f.Contains("wo="));
if (woItem != null)
{
wo[i] = woItem.Replace("wo=", "").Trim();
SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
}
string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
if (modelItem != null)
{
model_num[i] = modelItem.Replace("model_num=", "").Trim();
SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
}
string colorItem = item.FirstOrDefault(f => f.Contains("color="));
if (colorItem != null)
{
color[i] = colorItem.Replace("color=", "").Trim();
SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
}
string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
if (ppidItem != null)
{
ppid[i] = ppidItem.Replace("ppid=", "").Trim();
SendTaskMessage($"ppid:{ppid[i]}", MessageLevel.Info);
}
string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
if (revisionItem != null)
{
revision[i] = revisionItem.Replace("stage=", "").Trim();
SendTaskMessage($"stage:{revision[i]}", MessageLevel.Info);
}
LogHelper.WriteLogMes($"第二次询问mes成功");
SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
//切换产品
if (i == 0)//i=0时才会切换产品
{
var q = await _systemDatabaseService.QueryProductFromMesAsync(model_num[i], color[i]);
if (q != null && !string.IsNullOrEmpty(q.ProductName))
{
ProductList = new ObservableCollection(_productService.GetAllProducts());
Guid productId = Guid.Empty;
foreach (var itt in ProductList)
{
if (itt.Name == q.ProductName)
{
productId = itt.ID;
break;
}
}
if (currentProduct.ID != productId)
{
//切换产品
await _productService.LoadProductAsync(productId);
_productService.SetCurrentProduct(productId);
await WriteParameterToPlcExAsync();
SendTaskMessage($"切换产品成功", MessageLevel.Debug);
}
}
}
//产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
}
else
{
LogHelper.WriteLogMes($"第二次询问mes失败");
SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error);
//产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
if (models)
{
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
}
else
{
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
}
}
}
else
{
LogHelper.WriteLogMes($"询问mes失败");
SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
//产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
//表示前面的kb screw站点已经打完螺丝并且过站成功到3d aoi,根据plc的需求直接放行
if (response.Contains("2D AOI"))
{
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
return;
}
//表示kb组装 站点ng 具体写入型号跟plc对接
else if (response.Contains("C_KB_Link"))
{
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)5);
return;
}
// 后面还有一些判断条件也需跟当前AE对接
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
}
LogHelper.WriteLogMes($"【mes结束QUERY】");
SendTaskMessage($"mes结束QUERY", MessageLevel.Info);
}
else
{
ProductMainSN[i] = DateTime.Now.ToString("yyyyMMddHHmmssfff");
if (i == 0)
{
_productService.CurrentProductCode = ProductMainSN[i];
}
LogHelper.WriteLogMes($"【未启用mes】");
SendTaskMessage($"未启用mes", MessageLevel.Info);
await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
}
}
else
{
SendTaskMessage($"{i}过站检查信号:state={state},处理为默认状态", MessageLevel.Alarm);
await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)0);
return;
}
// _LastMesCheck[i] = 0;
}
}
}
}
}
catch (Exception ex)
{
SendTaskMessage(ex.Message, MessageLevel.Error);
}
}
#endregion
#region 方法
public void SendTaskMessage(string msg, MessageLevel level)
{
App.Current.Dispatcher.Invoke(() =>
{
_eventAggregator.GetEvent().Publish(new Models.MessageStruct() { Message = msg, level = level });
});
}
#region 界面状态显示
// 新增字段
private readonly object _runStatusLock = new object();
private RunDisplayMode _lastRunDisplayMode = RunDisplayMode.Unknown;
///
/// 刷新当前运行状态
///
public void CurrentRunStatus()
{
lock (_runStatusLock)
{
RunDisplayMode mode;
try
{
mode = EvaluateRunDisplayMode();
}
catch (Exception ex)
{
LogHelper.WriteLogError("刷新运行状态时出错", ex);
mode = RunDisplayMode.Manual;
}
ApplyRunDisplayMode(mode);
}
}
///
/// 计算当前应该显示的模式
///
private RunDisplayMode EvaluateRunDisplayMode()
{
// 1. 流线模式优先
if (_LastClearMode)
{
return RunDisplayMode.Clear;
}
// 2. 调机模式次优先
if (_LastManualMode)
{
return RunDisplayMode.Manual;
}
// 3. 没进自动运行,默认按调机显示
if (!_LastAutoRunning)
{
return RunDisplayMode.Manual;
}
// 4. 自动运行时,再检查前置条件
bool connectStatus = IsAllDeviceConnected();
bool mesStatus = IsMesReady();
bool paraStatus = IsParameterReady();
var curUser = _systemDatabaseService.GetCurrentUser();
if (connectStatus && mesStatus && paraStatus && curUser.userPart == UserPart.Operator)
{
return RunDisplayMode.Auto;
}
return RunDisplayMode.Manual;
}
///
/// 所有设备是否连接正常
///
private bool IsAllDeviceConnected()
{
List statusSnapshot = null;
App.Current.Dispatcher.Invoke(() =>
{
statusSnapshot = Status?.ToList() ?? new List();
});
if (statusSnapshot.Count == 0)
{
return false;
}
foreach (var item in statusSnapshot)
{
// 注意:这里不要直接 item.Background == Brushes.Red
if (item?.Background is SolidColorBrush brush)
{
bool res = true;
App.Current.Dispatcher.Invoke(() =>
{
if (brush.Color == Colors.Red)
{
SendTaskMessage("当前设备存在连接失败,请检查..." + item.Message, MessageLevel.Error);
res = false;
}
});
if (!res)
{
return false;
}
}
}
return true;
}
///
/// MES相关配置是否满足
///
private bool IsMesReady()
{
var allDevices = _configService.GetAllDeviceInfos();
for (int i = 0; i < ((allDevices.Count >= 8) ? 2 : 1); i++)
{
var stationConfig = allDevices.FirstOrDefault(d => d.DeviceNo == (i == 0 ? 0 : 7));
if (stationConfig == null)
{
SendTaskMessage("当前MES未配置,请检查...", MessageLevel.Error);
return false;
}
if (string.IsNullOrEmpty(stationConfig.SaveCsvPath) && i == 0)
{
SendTaskMessage("当前CSV路径为空,请检查...", MessageLevel.Error);
return false;
}
if (!stationConfig.EnableMES || !stationConfig.SaveCsv)
{
SendTaskMessage("当前未启用MES或未启用CSV保存,请检查...", MessageLevel.Error);
return false;
}
}
return true;
}
///
/// 参数相关配置是否满足
///
private bool IsParameterReady()
{
var systemConfig = _configService.GetSystemConfiguration();
if (systemConfig == null)
{
SendTaskMessage("当前无系统文件,请检查...", MessageLevel.Error);
return false;
}
if (!systemConfig.IsSaveScrewCurve)
{
SendTaskMessage("当前保存扭力曲线未勾选,请检查...", MessageLevel.Error);
return false;
}
if (string.IsNullOrEmpty(systemConfig.ScrewCurvePath))
{
SendTaskMessage("当前保存扭力曲线为空,请检查...", MessageLevel.Error);
return false;
}
return true;
}
///
/// 应用显示模式到UI
///
private void ApplyRunDisplayMode(RunDisplayMode mode)
{
// 模式没变化就不重复刷UI,也不重复打日志
if (_lastRunDisplayMode == mode)
{
return;
}
_lastRunDisplayMode = mode;
App.Current.Dispatcher.Invoke(() =>
{
switch (mode)
{
case RunDisplayMode.Auto:
CurrentDisplayMode = mode;
SendTaskMessage("当前为运行模式", MessageLevel.Debug);
break;
case RunDisplayMode.Clear:
CurrentDisplayMode = mode;
SendTaskMessage("当前为流线模式", MessageLevel.Info);
break;
case RunDisplayMode.Manual:
default:
CurrentDisplayMode = mode;
SendTaskMessage("当前为调机模式", MessageLevel.Info);
break;
}
});
}
#endregion
#region 压力采集
private float _maxPressure = float.MinValue;
///
/// 开始采集压力值
///
/// 配方名称
/// 产品编号
/// 锁付点编号
private void StartPressureCollection(string productName, string productCode, int positionIndex)
{
try
{
// cancel existing if any
//_pressureCts?.Cancel();
//_pressureTimeoutCts?.Cancel();
//_pressureLinkedCts?.Dispose();
//_pressureSamples?.Clear();
//_pressureTimeoutCts[positionIndex]?.Cancel();
_pressureSamples?.Clear();
_pressureCts[positionIndex] = new CancellationTokenSource();
_pressureTimeoutCts[positionIndex] = new CancellationTokenSource();
//_pressureCts = new CancellationTokenSource();
//_pressureTimeoutCts = new CancellationTokenSource();
// timeout after configured seconds
_pressureTimeoutCts[positionIndex].CancelAfter(TimeSpan.FromSeconds(_pressureMaxDurationSeconds));
// link tokens so either stop or timeout cancels
_pressureLinkedCts[positionIndex] = CancellationTokenSource.CreateLinkedTokenSource(_pressureCts[positionIndex].Token, _pressureTimeoutCts[positionIndex].Token);
var token = _pressureLinkedCts[positionIndex].Token;
var addressConfig = _configService.GetPlcAddresses();
var plc = _plcService.GetPlcByNumber(addressConfig.PlcNo) as OpcUaClientPLC;
if (plc == null || !plc.IsConnected) return;
Task.Run(async () =>
{
SendTaskMessage($"Pressure collection started for position {positionIndex}", MessageLevel.Debug);
while (!token.IsCancellationRequested)
{
try
{
float pressure = plc.ReadNode(addressConfig.In_PressureValue.Address);
lock (_pressureSamples)
{
//SendTaskMessage($"压力采集时出错:{pressure}", MessageLevel.Info);
_pressureSamples.Add((DateTime.Now, pressure));
if (pressure > _maxPressure)
{
_maxPressure = pressure;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogError("采集压力值时出错", ex);
}
await Task.Delay(_pressureSampleIntervalMs);
}
SendTaskMessage($"Pressure {_pressureSamples}", MessageLevel.Debug);
// if cancelled due to timeout
//if (_pressureTimeoutCts[positionIndex].IsCancellationRequested && !_pressureCts[positionIndex].IsCancellationRequested)
//{
// SendTaskMessage("Pressure collection timed out, saving collected data.", MessageLevel.Alarm);
// await StopAndSavePressureCollectionAsync(productName, productCode, positionIndex);
//}
}, token);
}
catch (Exception ex)
{
LogHelper.WriteLogError("StartPressureCollection error", ex);
SendTaskMessage($"压力采集时出错:{ex.Message}", MessageLevel.Error);
}
}
///
/// 停止采集并保存压力数据
///
///
///
///
///
private async Task StopAndSavePressureCollectionAsync(string productName, string productCode, int positionIndex)
{
try
{
// 获取当前产品
var currentProduct = _productService.GetCurrentProduct();
if (currentProduct == null)
{
return;
}
// cancel sampling
//_pressureCts?.Cancel();
// allow worker to observe cancel
//await Task.Delay(50);
List<(DateTime Timestamp, float Value)> snapshot;
lock (_pressureSamples)
{
snapshot = new List<(DateTime, float)>(_pressureSamples);
}
if (snapshot.Count == 0)
{
SendTaskMessage("No pressure samples collected.", MessageLevel.Info);
return;
}
// save to file under configured path
try
{
//获取系统参数
var systemConfig = _configService.GetSystemConfiguration();
string datePath = DateTime.Now.ToString("yyyy-MM") + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\";
string dirPath = System.IO.Path.Combine(systemConfig.ScrewCurvePath, datePath, currentProduct.Name, positionIndex.ToString());
if (!Directory.Exists(dirPath))
{
Directory.CreateDirectory(dirPath);
}
string fileName = $"pressure-{productCode}-{positionIndex}-{DateTime.Now.ToString("HHmmss")}.csv";
string fullPath = Path.Combine(dirPath, fileName);
string pngfileName = $"pressure-{productCode}-{positionIndex}-{DateTime.Now.ToString("HHmmss")}.png";
string pngPath = Path.Combine(dirPath, pngfileName);
if (!systemConfig.IsSaveScrewCurve)
{
fullPath = string.Empty;
pngPath = string.Empty;
}
App.Current.Dispatcher.Invoke(() =>
{
_eventAggregator.GetEvent().Publish((true, snapshot.ToArray(), pngPath));
});
//判断是否保存锁付曲线
if (systemConfig.IsSaveScrewCurve)
{
if (!string.IsNullOrEmpty(fullPath))
{
var sb = new StringBuilder();
sb.AppendLine("Timestamp,Value");
foreach (var s in snapshot)
{
sb.AppendLine($"{s.Timestamp:O},{s.Value}");
}
File.WriteAllText(fullPath, sb.ToString());
SendTaskMessage($"Pressure samples saved to {fullPath}", MessageLevel.Debug);
}
}
//var sb = new StringBuilder();
//sb.AppendLine("Timestamp,Value");
//foreach (var s in snapshot)
//{
// sb.AppendLine($"{s.Timestamp:O},{s.Value}");
//}
//File.WriteAllText(fullPath, sb.ToString());
// SendTaskMessage($"Pressure samples saved to {fullPath}", MessageLevel.Debug);
}
catch (Exception ex)
{
LogHelper.WriteLogError("保存压力采样文件时出错", ex);
SendTaskMessage("保存压力采样文件时出错: " + ex.Message, MessageLevel.Error);
}
}
catch (Exception ex)
{
LogHelper.WriteLogError("StopAndSavePressureCollectionAsync error", ex);
}
finally
{
try
{
_pressureLinkedCts[positionIndex]?.Cancel();
_pressureLinkedCts[positionIndex]?.Dispose();
_pressureTimeoutCts[positionIndex]?.Dispose();
_pressureCts[positionIndex]?.Dispose();
_pressureSamples?.Clear();
//_maxPressure = 0;
}
catch { }
}
}
#endregion
#region 产品记录
private async void DoYieldTime(object state)
{
try
{
//获取当前产品
var currentproduct = _productService.GetCurrentProduct();
if (currentproduct == null) return;
TotalQuantity = await _systemDatabaseService.GetOverallProductionAsync(currentproduct.Name);
//TotalQuantityToday = await _systemDatabaseService.GetTodayProductionAsync(currentproduct.Name);
//CurrentUPH = await _systemDatabaseService.GetLastHourProductionAsync(currentproduct.Name);
////
ThrowNumber = await _systemDatabaseService.GetThrowNumberAsync();
//NgNumber = await _systemDatabaseService.GetNGProductionAsync(currentproduct.Name);
//Yield = ((double)TotalQuantityToday - (double)NgNumber) / (double)TotalQuantityToday * 100;
TotalQuantityToday = 0;
CurrentUPH = 0;
NgNumber = 0;
Yield = 0;
ObservableCollection ProductList = new ObservableCollection(_productService.GetAllProducts());
foreach (var product in ProductList)
{
//TotalQuantity += await _systemDatabaseService.GetOverallProductionAsync(product.Name);
TotalQuantityToday += await _systemDatabaseService.GetTodayProductionAsync(product.Name);
CurrentUPH += await _systemDatabaseService.GetLastHourProductionAsync(product.Name);
NgNumber += await _systemDatabaseService.GetNGProductionAsync(product.Name);
}
Yield += ((double)TotalQuantityToday - (double)NgNumber) / (double)TotalQuantityToday * 100;
}
catch (Exception)
{
}
}
#endregion
#region 字符判定
///
/// 判断是否符合当站
///
///
///
///
private bool CheckCode(string data, string checkData)
{
try
{
if (data.Length != checkData.Length)
{
var len = checkData.Length - data.Length;
LogHelper.WriteLogMes($"判断是否符合当站失败,字符长度差{len}");
SendTaskMessage($"判断是否符合当站失败,字符长度差{len}", MessageLevel.Error);
return false;
}
int count = checkData.Length;
for (int i = 0; i < count; i++)
{
if (checkData[i] == '*')//不需要检测的部分
{
continue;
}
if (data[i] != checkData[i])//有值不一致
{
LogHelper.WriteLogMes($"判断是否符合当站失败,第{i}个字符,不一致");
SendTaskMessage($"判断是否符合当站失败,第{i}个字符,不一致", MessageLevel.Error);
return false;
}
}
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogMes($"判断是否符合当站失败,{ex}");
SendTaskMessage($"判断是否符合当站失败,{ex}", MessageLevel.Error);
return false;
}
}
#endregion
#region vm启动检测
///
/// 杀死所有VM有关进程
///
public void KillVMProcess()
{
KillProcess("VisionMasterServerApp");
KillProcess("VisionMaster");
KillProcess("VmModuleProxy.exe");
KillProcess("vServerApp.exe");
}
///
/// 杀死指定进程名的进程
///
///
public void KillProcess(string strKillName)
{
foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
{
if (p.ProcessName.Contains(strKillName))
{
try
{
p.Kill();
p.WaitForExit();
}
catch (Exception ex)
{
LogHelper.WriteLogError(string.Format(Lang.杀死进程0时失败, strKillName), ex);
}
}
}
}
#endregion
#region 拍螺丝数据保存csv
///
/// 通用方法:将存储XYU的字典导出为规范CSV文件
///
/// 存储XYU的字典
/// CSV文件保存路径
/// 是否追加模式(true=每次运行新增一行,false=覆盖原有文件)
/// 数值保留的小数位数(默认4位,适配工业视觉精度)
static void ExportDictionaryToCsv(Dictionary dataDict, string savePath, bool appendMode = false, int decimalPlaces = 4)
{
// 自动创建目录(避免路径不存在报错)
string directory = Path.GetDirectoryName(savePath);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 构建CSV内容
StringBuilder csvContent = new StringBuilder();
// 处理表头:仅当文件不存在/非追加模式时,才写入表头
if (!appendMode || !File.Exists(savePath))
{
csvContent.AppendLine(string.Join(",", dataDict.Keys));
}
// 处理数据行:按指定小数位数格式化数值
List formattedValues = new List();
foreach (var value in dataDict.Values)
{
formattedValues.Add(Math.Round(value, decimalPlaces).ToString());
}
csvContent.AppendLine(string.Join(",", formattedValues));
// 写入文件:UTF-8带BOM编码,Excel打开无乱码
if (appendMode)
{
// 追加模式:在文件末尾新增内容
File.AppendAllText(savePath, csvContent.ToString(), Encoding.UTF8);
}
else
{
// 覆盖模式:新建/替换文件
File.WriteAllText(savePath, csvContent.ToString(), Encoding.UTF8);
}
}
public bool ExportDictionaryToCsv1(string rfid, double x, double y, double u)
{
try
{
DateTime dt = DateTime.Now;
if (rfid == null || rfid.Count() == 0)
{
rfid = dt.ToString("MM-dd-mm");
}
string path = $"D:\\视觉检测数据XYU数据\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}\\{rfid}.csv";
StringBuilder sb = new StringBuilder();
sb.AppendLine($"SN,X,Y,U");
sb.AppendLine();
sb.AppendLine($"{rfid},{x},{y},{u}");
sb.AppendLine();
if (!Directory.Exists(Path.GetDirectoryName(path))) //判断文件夹是否
{
Directory.CreateDirectory(Path.GetDirectoryName(path)); //创建文件夹
}
if (File.Exists(path))
{
// 使用 StreamWriter 以追加模式打开文件
StreamWriter sw = new StreamWriter(path, true, Encoding.UTF8);
sw.WriteLine(sb.ToString().TrimEnd(','));
//sw.WriteLine(sb1.ToString().TrimEnd(','));
sw.Close();
sw.Dispose();
}
else
{
StreamWriter sw = new StreamWriter(path, true, Encoding.UTF8);
sw.WriteLine(sb.ToString().TrimEnd(','));
sw.Close();
sw.Dispose();
}
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogError("写入产品记录时出错!", ex);
return false;
}
}
#endregion
#region 产品锁付记录进行存储
///
/// 产品锁付记录进行存储
///
/// 结果
public void WriteProductLog(List lockResults, string rfid, double ct)
{
try
{
string result = "";//产品的总结果
foreach (var item in lockResults)
{
result = "OK";
if (!item.LockPassed)
{
result = "NG";
break;
}
}
DateTime dt = DateTime.Now;
int number = lockResults.Count;//螺丝数量
string path = $"D:\\Data\\生产数据\\{dt.ToString("yyyy-MM")}\\{dt.ToString("yyyy-MM-dd")}.csv";
StringBuilder sb = new StringBuilder();
//具体的ct需要优化
sb.Append($"{dt.ToString("yyyy-MM-dd HH:mm:ss")},{lockResults[0].ProductName},{Math.Round(ct, 3)},{rfid},{result},{lockResults[number - 1].NozzleNumber},{lockResults[number - 1].HeadNumber},");
foreach (var item in lockResults)
{
try
{
string res = "";
if (item.LockPassed)
{
res = "Pass";
}
else res = "Fail";
sb.Append($"{res},{item.ResultDetails},{item.LockTorque},{item.TargetTorqueUpperLimit},{item.TargetTorqueLowerLimit},{item.LockTurns},{item.TargetTurnsUpperLimit},{item.TargetTurnsLowerLimit},{item.Pressure},{item.PositionX},{item.PositionY},{item.StartPositionZ},{item.EndPositionZ},{item.LockDescentSpeed},");
}
catch (Exception)
{
sb.Append($",,,,,,,,,,,,,,");
}
}
//========================
if (!Directory.Exists(Path.GetDirectoryName(path))) //判断文件夹是否
{
Directory.CreateDirectory(Path.GetDirectoryName(path)); //创建文件夹
}
if (File.Exists(path))
{
// 使用 StreamWriter 以追加模式打开文件
StreamWriter sw = new StreamWriter(path, true, Encoding.UTF8);
sw.WriteLine(sb.ToString().TrimEnd(','));
sw.Close();
sw.Dispose();
}
else
{
StreamWriter sw = new StreamWriter(path, true, Encoding.UTF8);
StringBuilder sb1 = new StringBuilder();
StringBuilder sb_up = new StringBuilder();
StringBuilder sb_down = new StringBuilder();
sb1.Append("时间,产品名称,CT,RFID,产品结果,吸嘴数量,披头数量,");
for (int i = 0; i < number; i++)
{
sb1.Append($"螺丝{i + 1}结果,螺丝{i + 1}结果详情,螺丝{i + 1}扭矩,螺丝{i + 1}扭矩上限,螺丝{i + 1}扭矩下限,螺丝{i + 1}圈数,螺丝{i + 1}圈数上限,螺丝{i + 1}圈数下限,螺丝{i + 1}压力值,螺丝{i + 1}锁附X,螺丝{i + 1}锁附Y,螺丝{i + 1}开始位置Z,螺丝{i + 1}结束位置Z,螺丝{i + 1}锁附下降速度,");
}
sw.WriteLine(sb1.ToString().TrimEnd(','));
sw.WriteLine(sb.ToString().TrimEnd(','));
sw.Close();
sw.Dispose();
}
}
catch (Exception ex)
{
LogHelper.WriteLogError("写入产品记录时出错!", ex);
}
lockResultsAll.Clear();
}
///
/// 记录当前工位的锁付结果
///
///
///
///
///
///
///
/// 是否将所有螺丝的锁付结果一行显示,还是每颗螺丝单独一行显示,默认所有颗螺丝单独一行显示
///
private bool RecordLockResult(int stationIndex, List lockResults, string productMainSN, double ct, DateTime timestamp, string savePath, bool isSingleLine = true, bool isFirstEnter = true)
{
try
{
//获取当前工位的配置
var stationConfig = _configService.GetDeviceInfo(stationIndex);
if (stationConfig == null) return false;
//如果保存路径为空,则不保存
if (string.IsNullOrEmpty(savePath)) return false;
//如果锁付结果列表为空,则不保存
if (lockResults == null || lockResults.Count == 0) return false;
string fullPath = savePath;
//如果保存路径是一个文件路径,则直接保存,否则按照工位和日期进行保存
if (File.Exists(savePath) || Path.HasExtension(savePath))
{
fullPath = savePath;
//如果路径不存在,则创建
// 创建目录
string directory = Path.GetDirectoryName(fullPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
}
else
{
//锁付结果保存至CSV文件,文件名格式:savePath\yyyy-MM\工位编号_工位名称_yyyy-MM-dd.csv
string datePath = timestamp.ToString("yyyy-MM") + "\\";
string dirPath = Path.Combine(savePath, datePath);
if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
string fileName = $"{stationIndex}_{stationConfig.DeviceName}_{timestamp.ToString("yyyy-MM-dd")}.csv";
fullPath = Path.Combine(dirPath, fileName);
}
StringBuilder sb = new StringBuilder();
if (!File.Exists(fullPath))
{
if (isSingleLine)
{
//将所有螺丝的锁付结果一行显示,列名格式:时间,产品主SN,CT,工位编号,工位名称,配方名称,配方编号,螺丝编号1,锁付结果1,锁付圈数1,锁付扭矩1,锁付时长1,电批程序编号1,结果详情1,压力值1,扭矩上限1,扭矩下限1,圈数上限1,圈数下限1,电批锁付速度1,锁附X1,锁附Y1,开始位置Z1,结束位置Z1,锁附下降速度1,螺丝供料器编号,...(依次类推直到螺丝编号n)
sb.Append("时间,产品主SN,CT,工位编号,工位名称,配方名称,配方编号,");
for (int i = 0; i < lockResults.Count; i++)
{
sb.Append($"螺丝编号{i + 1},锁付结果{i + 1},锁付圈数{i + 1},锁付扭矩{i + 1},锁付时长{i + 1},电批程序编号{i + 1},结果详情{i + 1},压力值{i + 1}," +
$"扭矩上限{i + 1},扭矩下限{i + 1},圈数上限{i + 1},圈数下限{i + 1},电批锁付速度{i + 1},锁附X{i + 1},锁附Y{i + 1},开始位置Z{i + 1},结束位置Z{i + 1},锁附下降速度{i + 1},螺丝供料器编号{i + 1},");
}
sb.Append("吸嘴编号,披头编号,操作员姓名");
}
else
{
sb.Append("时间,产品主SN,CT,工位编号,工位名称,配方名称,配方编号,螺丝编号,锁付结果,锁付圈数,锁付扭矩,锁付时长,电批程序编号,结果详情,压力值," +
"扭矩上限,扭矩下限,圈数上限,圈数下限,电批锁付速度,锁附X,锁附Y,开始位置Z,结束位置Z,锁附下降速度,螺丝供料器编号,吸嘴编号,披头编号,操作员姓名");
}
sb.AppendLine();
}
if (isSingleLine)
{
//将所有螺丝的锁付结果一行显示,列名格式:时间,产品主SN,CT,工位编号,工位名称,配方名称,配方编号,螺丝编号1,锁付结果1,锁付圈数1,锁付扭矩1,锁付时长1,电批程序编号1,结果详情1,压力值1,扭矩上限1,扭矩下限1,圈数上限1,圈数下限1,电批锁付速度1,锁附X1,锁附Y1,开始位置Z1,结束位置Z1,锁附下降速度1,螺丝供料器编号,...(依次类推直到螺丝编号n)
sb.Append($"{timestamp:yyyy-MM-dd HH:mm:ss},{productMainSN},{Math.Round(ct, 3)},{stationIndex},{stationConfig.DeviceName},{lockResults[0].ProductName},{lockResults[0].ProductNumber},");
for (int i = 0; i < lockResults.Count; i++)
{
var item = lockResults[i];
sb.Append($"螺丝{i + 1},{item.LockPassed},{item.LockTurns},{item.LockTorque},{item.LockDuration},{item.ScrewdriverProgramNumber}," +
$"{item.ResultDetails},{item.Pressure},{item.TargetTorqueUpperLimit},{item.TargetTorqueLowerLimit},{item.TargetTurnsUpperLimit},{item.TargetTurnsLowerLimit},{item.TargetLockSpeed}," +
$"{item.PositionX},{item.PositionY},{item.StartPositionZ},{item.EndPositionZ},{item.LockDescentSpeed},{item.FeederNumber},");
}
sb.Append($"{lockResults[lockResults.Count - 1].NozzleNumber},{lockResults[lockResults.Count - 1].HeadNumber},{lockResults[lockResults.Count - 1].OperatorName}");
sb.AppendLine();
}
else
{
foreach (var item in lockResults)
{
sb.Append($"{timestamp:yyyy-MM-dd HH:mm:ss},{productMainSN},{Math.Round(ct, 3)},{stationIndex},{stationConfig.DeviceName},{item.ProductName},{item.ProductNumber},{item.Number},{item.LockPassed},{item.LockTurns},{item.LockTorque},{item.LockDuration},{item.ScrewdriverProgramNumber}," +
$"{item.ResultDetails},{item.Pressure},{item.TargetTorqueUpperLimit},{item.TargetTorqueLowerLimit},{item.TargetTurnsUpperLimit},{item.TargetTurnsLowerLimit},{item.TargetLockSpeed}," +
$"{item.PositionX},{item.PositionY},{item.StartPositionZ},{item.EndPositionZ},{item.LockDescentSpeed},{item.FeederNumber},{item.NozzleNumber},{item.HeadNumber},");
sb.AppendLine();
}
}
// 使用 StreamWriter 以追加模式打开文件
StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8);
sw.Write(sb.ToString());
sw.Close();
sw.Dispose();
return true;
}
catch (Exception ex)
{
if (!isFirstEnter)
return false;
LogHelper.WriteLogError("记录锁付结果时出错!", ex);
//我希望在这里生成一个临时文件来保存这次的记录,因为如果本地文件被打开时可能会导致无法写入,如果不保存的话可能会丢失这次的记录,所以我想在这里生成一个临时文件来保存这次的记录
//生成临时文件的路径,格式为savePath\yyyy-MM\工位编号_yyyy-MM-dd_temp\工位编号_yyyy-MM-dd_HH-mm-ss-fff_temp.csv
string datePath = timestamp.ToString("yyyy-MM") + "\\";
string tempDirPath = Path.Combine(savePath, datePath, $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd")}_temp");
string tempFileName = $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd_HH-mm-ss-fff")}_temp.csv";
RecordLockResult(stationIndex, lockResults, productMainSN, ct, timestamp, tempFileName, isSingleLine, false);
return false;
}
}
///
/// 记录其他工位的结果,其他工位要存储的数据项按照键值对集合的形式传入,键为数据项名称,值为数据项的值
///
///
///
///
///
///
///
///
///
private bool RecordOtherStationResult(int stationIndex, Dictionary dataItems, string productMainSN, double ct, DateTime timestamp, string savePath, bool isFirstEnter = true)
{
try
{
//获取当前工位的配置
var stationConfig = _configService.GetDeviceInfo(stationIndex);
if (stationConfig == null) return false;
// 获取当前产品
var currentProduct = _productService.GetCurrentProduct();
if (currentProduct == null)
{
return false;
}
//如果保存路径为空,则不保存
if (string.IsNullOrEmpty(savePath)) return false;
string fullPath = savePath;
//如果保存路径是一个文件路径,则直接保存,否则按照工位和日期进行保存
if (File.Exists(savePath) || Path.HasExtension(savePath))
{
fullPath = savePath;
//如果路径不存在,则创建
// 创建目录
string directory = Path.GetDirectoryName(fullPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
}
else
{
//结果保存至CSV文件,文件名格式:savePath\yyyy-MM\工位编号_工位名称_yyyy-MM-dd.csv
string datePath = timestamp.ToString("yyyy-MM") + "\\";
string dirPath = Path.Combine(savePath, datePath);
if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
string fileName = $"{stationIndex}_{stationConfig.DeviceName}_{timestamp.ToString("yyyy-MM-dd")}.csv";
fullPath = Path.Combine(dirPath, fileName);
}
StringBuilder sb = new StringBuilder();
if (!File.Exists(fullPath))
{
sb.Append("时间,产品主SN,CT,工位编号,工位名称,配方名称,配方编号,");
foreach (var item in dataItems)
{
sb.Append($"{item.Key},");
}
sb.AppendLine();
}
sb.Append($"{timestamp:yyyy-MM-dd HH:mm:ss},{productMainSN},{Math.Round(ct, 3)},{stationIndex},{stationConfig.DeviceName},{currentProduct.Name},{currentProduct.NumberCode},");
foreach (var item in dataItems)
{
sb.Append($"{item.Value},");
}
sb.AppendLine();
// 使用 StreamWriter 以追加模式打开文件
StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8);
sw.Write(sb.ToString());
sw.Close();
sw.Dispose();
return true;
}
catch (Exception ex)
{
if (!isFirstEnter)
{
return false;
}
LogHelper.WriteLogError("记录其他工位结果时出错!", ex);
//我希望在这里生成一个临时文件来保存这次的记录,因为如果本地文件被打开时可能会导致无法写入,如果不保存的话可能会丢失这次的记录,所以我想在这里生成一个临时文件来保存这次的记录
//生成临时文件的路径,格式为savePath\yyyy-MM\工位编号_yyyy-MM-dd_temp\工位编号_yyyy-MM-dd_HH-mm-ss-fff_temp.csv
string datePath = timestamp.ToString("yyyy-MM") + "\\";
string tempDirPath = Path.Combine(savePath, datePath, $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd")}_temp");
string tempFileName = $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd_HH-mm-ss-fff")}_temp.csv";
RecordOtherStationResult(stationIndex, dataItems, productMainSN, ct, timestamp, tempFileName, false);
return false;
}
}
///
/// 记录工位的结果,其他工位要存储的数据项按照键值对集合的形式传入,键为数据项名称,值为数据项的值
///
///
///
///
///
///
///
///
///
private bool RecordStationResult(int stationIndex, IEnumerable dfcs, string productMainSN, double ct, DateTime timestamp, string savePath, bool isFirstEnter = true)
{
try
{
//获取当前工位的配置
var stationConfig = _configService.GetDeviceInfo(stationIndex);
if (stationConfig == null) return false;
//如果保存路径为空,则不保存
if (string.IsNullOrEmpty(savePath)) return false;
string fullPath = savePath;
//如果保存路径是一个文件路径,则直接保存,否则按照工位和日期进行保存
if (!File.Exists(savePath) || !Path.HasExtension(savePath))
{
fullPath = savePath;
//如果路径不存在,则创建
// 创建目录
string directory = Path.GetDirectoryName(fullPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
}
//结果保存至CSV文件,文件名格式:savePath\yyyy-MM\工位编号_工位名称_yyyy-MM-dd.csv
string datePath = timestamp.ToString("yyyy-MM") + "\\";
string dirPath = Path.Combine(savePath, datePath);
if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
string fileName = $"{stationIndex}_{stationConfig.DeviceName}_{timestamp.ToString("yyyy-MM-dd")}.csv";
fullPath = Path.Combine(dirPath, fileName);
StringBuilder sb = new StringBuilder();
if (!File.Exists(fullPath))
{
sb.Append("PPID,Screw_Number,PROCESS_NAME,PROCESS_START_TIME,CTQ_NAME,CTQ_VALUE,CTQ_LSL,CTQ_USL,PROCESS_END_TIME,SUPPLIER_NAME,COMMODITY_TYPE,REVISION,WO#,MFG_LOT#," +
"MFG_ASSY_LINE,PROCESS_OUTCOME,PROCESS_MACHINE_ID,CTQ_UNIT_OF_MEASURE,ERROR_MESSAGE,PD_ATTR_01,PD_ATTR_02,PD_ATTR_03,PD_ATTR_04,PD_ATTR_05");
sb.AppendLine();
}
if (dfcs == null || dfcs.Count() == 0)
{
return false;
}
foreach (var dfc in dfcs)
{
sb.Append($"{dfc.PPID},{dfc.ScrewNum},{dfc.PROCESS_NAME},{dfc.PROCESS_START_TIME},{dfc.CTQ_NAME},{dfc.CTQ_VALUE:F3},{dfc.CTQ_LSL:F3},{dfc.CTQ_USL:F3},{dfc.PROCESS_END_TIME},{dfc.SUPPLIER_NAME},{dfc.COMMODITY_TYPE},{dfc.REVISION},{dfc.WO},{dfc.MFG_LOT},{dfc.MFG_ASSY_LINE},{dfc.PROCESS_OUTCOME},{dfc.PROCESS_MACHINE_ID},{dfc.CTQ_UNIT_OF_MEASURE},{dfc.ERROR_MESSAGE},{dfc.PD_ATTR_01},{dfc.PD_ATTR_02},{dfc.PD_ATTR_03},{dfc.PD_ATTR_04},{dfc.PD_ATTR_05}");
sb.AppendLine();
}
// 使用 StreamWriter 以追加模式打开文件
StreamWriter sw = new StreamWriter(fullPath, true, Encoding.UTF8);
sw.Write(sb.ToString());
sw.Close();
sw.Dispose();
return true;
}
catch (Exception ex)
{
if (!isFirstEnter)
{
return false;
}
LogHelper.WriteLogError("记录其他工位结果时出错!", ex);
//我希望在这里生成一个临时文件来保存这次的记录,因为如果本地文件被打开时可能会导致无法写入,如果不保存的话可能会丢失这次的记录,所以我想在这里生成一个临时文件来保存这次的记录
//生成临时文件的路径,格式为savePath\yyyy-MM\工位编号_yyyy-MM-dd_temp\工位编号_yyyy-MM-dd_HH-mm-ss-fff_temp.csv
string datePath = timestamp.ToString("yyyy-MM") + "\\";
string tempDirPath = Path.Combine(savePath, datePath, $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd")}_temp");
string tempFileName = $"{stationIndex}_{timestamp.ToString("yyyy-MM-dd_HH-mm-ss-fff")}_temp.csv";
RecordStationResult(stationIndex, dfcs, productMainSN, ct, timestamp, tempFileName, false);
return false;
}
}
#endregion
#region 写log
TxtFile mesLog = new TxtFile($"D:\\Data\\Mes记录", "");
public void MesMessage(string msg)
{
mesLog.WriteLine("== " + msg + "\r\n");
}
TxtFile infoLog = new TxtFile($"D:\\Data\\运行日志", "");
public void InfoMessage(string msg)
{
infoLog.WriteLine("== " + msg + "\r\n");
}
#endregion
#region 压缩图片文件夹
///
/// 压缩文件
///
/// 原路径
/// 压缩后文件的名称
/// mes中的station
/// mes中的fixtureID
public async void CompressionImage(string sourcePath, string zipname, string station, string fixid)
{
string zipPath = "";
try
{
SendTaskMessage($"开始压缩文件", MessageLevel.Info);
await Task.Run(() =>
{
zipPath = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}\\{zipname}.zip";//生成的zip文件路径
string path = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
ZipFile.CreateFromDirectory(sourcePath, zipPath);
});
}
catch (Exception ex)
{
LogHelper.WriteLogError($"压缩文件时出错", ex);
}
}
public async void CompressionSingleCsv(string csvFilePath, string zipname, string station, string fixid)
{
string zipPath = "";
try
{
SendTaskMessage("开始压缩CSV文件", MessageLevel.Info);
await Task.Run(() =>
{
if (!File.Exists(csvFilePath))
{
LogHelper.WriteLogMes($"CSV文件不存在,不压缩:{csvFilePath}");
return;
}
zipPath = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}\\{zipname}.zip";
string path = $"Z:\\{station}\\{fixid}\\{DateTime.Now.ToString("yyyyMMdd")}";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
using (FileStream fs = new FileStream(zipPath, FileMode.Create))
{
using (ZipArchive archive = new ZipArchive(fs, ZipArchiveMode.Create))
{
archive.CreateEntryFromFile(csvFilePath, Path.GetFileName(csvFilePath));
}
}
});
SendTaskMessage("CSV文件压缩完成", MessageLevel.Debug);
}
catch (Exception ex)
{
LogHelper.WriteLogError("压缩CSV文件失败", ex);
}
}
#endregion
///
/// 后台报警刷新显示(适配PLC结构化变量名Bool点位 OPC版)
/// 完全沿用你原有循环结构、防抖逻辑、UI报警推送规则
///
public void SendAlarmToUi(OpcUaClientPLC plc)
{
PlcExcelLoad loadData = new PlcExcelLoad();
loadData.LoadData(6);
// 增加报警状态缓存,防止循环重复刷屏报警
Dictionary alarmLastState = new Dictionary();
foreach (var alarm in loadData.AlaemList)
{
alarmLastState.Add(alarm.OpcAddress, true);
}
Task.Run(() =>
{
while (true)
{
try
{
Thread.Sleep(50);
foreach (var alarm in loadData.AlaemList)
{
// 调用你封装好的OPC读取Bool方法
bool plcState = plc.ReadBoolNode(alarm.OpcAddress);
// 上升沿防抖:只有状态从正常→异常(false)变化时,才推送一次报警
if (!plcState && alarmLastState[alarm.OpcAddress])
{
SendTaskMessage($"PLC报警信息:{alarm.Name}", MessageLevel.Error);
LogHelper.WriteLogPlcWarn($"PLC报警信息:{alarm.Name}");
alarmLastState[alarm.OpcAddress] = false;
}
// 报警复位:状态恢复正常,重置缓存
else if (plcState && !alarmLastState[alarm.OpcAddress])
{
alarmLastState[alarm.OpcAddress] = true;
}
}
}
catch
{
Thread.Sleep(200);
}
}
});
}
#endregion
}
}