AHAQ_QGNH/AnQing/ViewModel/MianWindowViewModel.cs

849 lines
32 KiB
C#
Raw Normal View History

2025-05-19 09:31:47 +08:00
using AnQing.Commons;
using AnQing.Models;
using Modbus.Device;
using MvCamCtrl.NET;
using RGD.DBUtility;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using ZXing;
using ZXing.Common;
using static System.Net.Mime.MediaTypeNames;
using Application = System.Windows.Application;
using Color = System.Drawing.Color;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
namespace AnQing.ViewModel
{
public class MianWindowViewModel : INotifyPropertyChanged
{
#region
/// <summary>
/// 图片识别区域
/// </summary>
public List<Rectangle> possibleRegions = new List<Rectangle>();
/// <summary>
/// 摄像头1连接状态
/// </summary>
private bool video1State;
public bool Video1State
{
get { return video1State; }
set
{
video1State = value;
OnPropertyChanged("Video1State");
}
}
/// <summary>
/// 摄像头2连接状态
/// </summary>
private bool video2State;
public bool Video2State
{
get { return video2State; }
set
{
video2State = value;
OnPropertyChanged("Video2State");
}
}
/// <summary>
/// 识别状态
/// </summary>
private bool readerState;
public bool ReaderState
{
get { return readerState; }
set
{
readerState = value;
OnPropertyChanged("ReaderState");
}
}
/// <summary>
/// 识别到的数据信息
/// </summary>
private string readerText;
public string ReaderText
{
get { return readerText; }
set
{
readerText = value;
OnPropertyChanged("ReaderText");
}
}
private string readerText_temp;
/// <summary>
/// 识别时间
/// </summary>
private string readerTextTime;
public string ReaderTextTime
{
get { return readerTextTime; }
set
{
readerTextTime = value;
OnPropertyChanged("ReaderTextTime");
}
}
/// <summary>
/// 图像一
/// </summary>
private ImageSource _imageSource;
public ImageSource ImageSource
{
get { return _imageSource; }
set
{
if (_imageSource != value)
{
if (_imageSource is BitmapSource oldBitmapSource)
{
DisposeBitmapSource(oldBitmapSource);
}
_imageSource = value;
OnPropertyChanged(nameof(ImageSource));
}
}
}
/// <summary>
/// 释放 BitmapSource 资源,避免 GDI 资源泄漏
/// </summary>
private void DisposeBitmapSource(BitmapSource bitmapSource)
{
if (bitmapSource == null) return;
if (bitmapSource is IDisposable disposable)
{
disposable.Dispose();
}
}
/// <summary>
/// 图像二
/// </summary>
private ImageSource _imageSource2;
public ImageSource ImageSource2
{
get { return _imageSource2; }
set
{
if (_imageSource2 != value)
{
_imageSource2 = value;
OnPropertyChanged(nameof(ImageSource2));
}
}
}
#endregion
#region
/// <summary>
/// 摄像触发采集
/// </summary>
private ICommand buttonGatherCommand;
public ICommand ButtonGatherCommand => buttonGatherCommand ??= new RelayCommand(TriggerExec);
/// <summary>
/// 释放
/// </summary>
private ICommand buttonCloseCommand;
public ICommand ButtonCloseCommand => buttonCloseCommand ??= new RelayCommand(CloseCamera);
#endregion
#region
//图像一
MyCamera.MV_CC_DEVICE_INFO_LIST m_stDeviceList = new MyCamera.MV_CC_DEVICE_INFO_LIST();
public MyCamera m_MyCamera = new MyCamera();
bool m_bGrabbing = false;
//图像二
public MyCamera m_MyCamera2 = new MyCamera();
bool m_bGrabbing2 = false;
/// <summary>
/// 关闭程序需要关闭摄像头
/// </summary>
private void CloseCamera()
{
// ch:关闭设备 | en:Close Device
m_MyCamera.MV_CC_CloseDevice_NET();
m_MyCamera.MV_CC_DestroyDevice_NET();
}
/// <summary>
/// 初始化打开摄像头
/// </summary>
private void OpenCamera()
{
string targetIp = "192.168.2.11"; // 摄像头IP
string targetIp2 = "192.168.3.13"; // 摄像头2 IP
bool ip1 = false, ip2 = false;
System.GC.Collect();
m_stDeviceList.nDeviceNum = 0;
int nRet1 = MyCamera.MV_CC_EnumDevices_NET(MyCamera.MV_GIGE_DEVICE | MyCamera.MV_USB_DEVICE, ref m_stDeviceList);
if (nRet1 != 0)
{
MessageBox.Show("MV_CC_EnumDevices_NET 初始化失败: " + nRet1);
return;
}
// ch:获取选择的设备信息 | en:Get selected device information
MyCamera.MV_CC_DEVICE_INFO device = new MyCamera.MV_CC_DEVICE_INFO();//相机一
MyCamera.MV_CC_DEVICE_INFO device2 = new MyCamera.MV_CC_DEVICE_INFO();//相机二
for (int i = 0; i < m_stDeviceList.nDeviceNum; i++)
{
MyCamera.MV_CC_DEVICE_INFO device_temp = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
if (device_temp.nTLayerType == MyCamera.MV_GIGE_DEVICE)
{
MyCamera.MV_GIGE_DEVICE_INFO_EX gigeInfo = (MyCamera.MV_GIGE_DEVICE_INFO_EX)MyCamera.ByteToStruct(device_temp.SpecialInfo.stGigEInfo, typeof(MyCamera.MV_GIGE_DEVICE_INFO_EX));
string deviceIp = $"{(gigeInfo.nCurrentIp >> 24) & 0xFF}.{(gigeInfo.nCurrentIp >> 16) & 0xFF}.{(gigeInfo.nCurrentIp >> 8) & 0xFF}.{gigeInfo.nCurrentIp & 0xFF}";
if (deviceIp == targetIp) // 相机一
{
device = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
ip1 = true;
}
if (deviceIp == targetIp2) // 相机二
{
device2 = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(m_stDeviceList.pDeviceInfo[i], typeof(MyCamera.MV_CC_DEVICE_INFO));
ip2 = true;
}
}
}
// ch:打开设备 | en:Open device
if (null == m_MyCamera)
{
m_MyCamera = new MyCamera();
if (null == m_MyCamera)
{
return;
}
}
if (null == m_MyCamera2)
{
m_MyCamera2 = new MyCamera();
if (null == m_MyCamera2)
{
return;
}
}
int nRet = m_MyCamera.MV_CC_CreateDevice_NET(ref device);
if (MyCamera.MV_OK != nRet)
{
return;
}
nRet = m_MyCamera.MV_CC_OpenDevice_NET();
if (MyCamera.MV_OK != nRet)
{
m_MyCamera.MV_CC_CloseDevice_NET();
m_MyCamera.MV_CC_DestroyDevice_NET();
//// ch:关闭设备 | en:Close Device
m_MyCamera2.MV_CC_CloseDevice_NET();
m_MyCamera2.MV_CC_DestroyDevice_NET();
return;
}
int nRet_2 = m_MyCamera2.MV_CC_CreateDevice_NET(ref device2);
if (MyCamera.MV_OK != nRet_2)
{
return;
}
nRet_2 = m_MyCamera2.MV_CC_OpenDevice_NET();
if (MyCamera.MV_OK != nRet_2)
{
m_MyCamera.MV_CC_CloseDevice_NET();
m_MyCamera.MV_CC_DestroyDevice_NET();
//// ch:关闭设备 | en:Close Device
m_MyCamera2.MV_CC_CloseDevice_NET();
m_MyCamera2.MV_CC_DestroyDevice_NET();
return;
}
// ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
if (device.nTLayerType == MyCamera.MV_GIGE_DEVICE)
{
int nPacketSize = m_MyCamera.MV_CC_GetOptimalPacketSize_NET();
if (nPacketSize > 0)
{
nRet = m_MyCamera.MV_CC_SetIntValueEx_NET("GevSCPSPacketSize", nPacketSize);
if (nRet != MyCamera.MV_OK)
{
MessageBox.Show("MV_CC_SetIntValueEx_NET 初始化失败: " + nRet);
}
}
else
{
MessageBox.Show("MV_CC_SetIntValueEx_NET 初始化失败: " + nPacketSize);
}
}
if (device2.nTLayerType == MyCamera.MV_GIGE_DEVICE)
{
int nPacketSize = m_MyCamera2.MV_CC_GetOptimalPacketSize_NET();
if (nPacketSize > 0)
{
nRet_2 = m_MyCamera2.MV_CC_SetIntValueEx_NET("GevSCPSPacketSize", nPacketSize);
if (nRet_2 != MyCamera.MV_OK)
{
MessageBox.Show("MV_CC_SetIntValueEx_NET 初始化失败: " + nRet_2);
}
}
else
{
MessageBox.Show("MV_CC_SetIntValueEx_NET 初始化失败: " + nPacketSize);
}
}
// ch:设置采集连续模式 | en:Set Continues Aquisition Mode
m_MyCamera.MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS);
m_MyCamera.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
m_MyCamera.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE);
m_MyCamera2.MV_CC_SetEnumValue_NET("AcquisitionMode", (uint)MyCamera.MV_CAM_ACQUISITION_MODE.MV_ACQ_MODE_CONTINUOUS);
m_MyCamera2.MV_CC_SetEnumValue_NET("TriggerMode", (uint)MyCamera.MV_CAM_TRIGGER_MODE.MV_TRIGGER_MODE_ON);
m_MyCamera2.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE);
//bnGetParam_Click(null, null);//获取相机焦距等参数
// ch:标志位置位true | en:Set position bit true
m_bGrabbing = true;
m_bGrabbing2 = true;
// ch:开始采集 | en:Start Grabbing
int nRet2 = m_MyCamera.MV_CC_StartGrabbing_NET();
if (MyCamera.MV_OK != nRet2)
{
m_bGrabbing = false;
MessageBox.Show("MV_CC_StartGrabbing_NET 初始化失败: " + nRet2);
return;
}
int nRet2_2 = m_MyCamera2.MV_CC_StartGrabbing_NET();
if (MyCamera.MV_OK != nRet2_2)
{
m_bGrabbing2 = false;
MessageBox.Show("MV_CC_StartGrabbing_NET 初始化失败: " + nRet2_2);
return;
}
if (ip1)
Video1State = true;
if (ip2)
Video2State = true;
}
private List<string> ReadImageGuid_Temp = new List<string> ();
public string ReaderImagePath_Temp;
/// <summary>
/// 抓拍图片
/// </summary>
public void ReceiveThreadProcess()
{
MyCamera.MV_FRAME_OUT stFrameInfo = new MyCamera.MV_FRAME_OUT();
int nRet = m_MyCamera.MV_CC_GetImageBuffer_NET(ref stFrameInfo, 1000);
if (nRet == MyCamera.MV_OK)
{
try
{
int width = stFrameInfo.stFrameInfo.nWidth;
int height = stFrameInfo.stFrameInfo.nHeight;
int stride = width; // Mono8: 每个像素1字节stride = width
// 直接利用内存地址创建 Bitmap 对象
Bitmap bitmap = new Bitmap(width, height, stride, PixelFormat.Format8bppIndexed, stFrameInfo.pBufAddr);
// 设置灰度调色板
ColorPalette palette = bitmap.Palette;
for (int i = 0; i < 256; i++)
{
palette.Entries[i] = Color.FromArgb(i, i, i);
}
bitmap.Palette = palette;
DateTime start = DateTime.Now;
var reader = new BarcodeReader
{
AutoRotate = true,
Options = new DecodingOptions
{
TryHarder = true, // 深度解析模式
PureBarcode = false, // 是否纯条码(无文字干扰)
AssumeCode39CheckDigit = false, // 是否验证Code39校验位
PossibleFormats = new List<BarcodeFormat>
{
BarcodeFormat.CODE_128
//BarcodeFormat.EAN_13,
//BarcodeFormat.EAN_8,
//BarcodeFormat.UPC_A,
//BarcodeFormat.UPC_E,
//BarcodeFormat.CODE_39,
//BarcodeFormat.ITF,
//BarcodeFormat.CODABAR
},
CharacterSet = "UTF-8"
}
};
foreach (var region in possibleRegions)
{
Bitmap cropped = CropBarcodeRegion(bitmap, region);
var result = reader.Decode(cropped);
cropped.Dispose();
cropped = null;
if (result != null)
{
readerText_temp = result.Text;
ReaderText = result.Text;
ReaderState = false;
break;
}
else
{
readerText_temp = "";
}
}
if (string.IsNullOrEmpty(readerText_temp))
{
ReaderText = "未检测到条码!";
ReaderState = true;
}
DateTime end = DateTime.Now;
TimeSpan duration = end - start;
ReaderTextTime = duration.TotalSeconds.ToString() + " 秒";
Application.Current.Dispatcher.Invoke(() =>
{
// 转换为 WPF BitmapImage 用于 Image 控件显示
ImageSource = Common.BitmapToBitmapSource2(bitmap);
});
string guid = Guid.NewGuid().ToString();
ReaderImagePath_Temp = AppDomain.CurrentDomain.BaseDirectory + "ImageList\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\" + guid + ".jpeg";
string imgpath_after = "";
string sql = @"
IF NOT EXISTS (SELECT 1 FROM SCAN_MIAN WHERE goodcode = @goodcode)
BEGIN
INSERT INTO SCAN_MIAN (time, goodcode, img, img_after, ex)
VALUES (@time, @goodcode, @img, @img_after, @ex)
END";
SqlParameter[] parameters = {
new SqlParameter("@time", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")),
new SqlParameter("@goodcode", readerText_temp),
new SqlParameter("@img", ReaderImagePath_Temp),
new SqlParameter("@img_after", imgpath_after),
new SqlParameter("@ex", guid)
};
DbHelperSQL.ExecuteSql(sql, parameters);
DbHelperSQL.ExecuteSql($"INSERT INTO SCAN_MIAN (time, goodcode, img, img_after,ex) VALUES ('{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}', '{readerText_temp}', '{ReaderImagePath_Temp}', '{imgpath_after}','{guid}');");
ReadImageGuid_Temp.Add(guid);
// 直接将图像数据保存到本地(例如保存为 JPEG 格式)
SaveBitmapToFile(bitmap, ReaderImagePath_Temp);
bitmap.Dispose();
bitmap = null;
}
catch (Exception ex)
{
MessageBox.Show("图像处理失败: " + ex.Message);
}
finally
{
m_MyCamera.MV_CC_FreeImageBuffer_NET(ref stFrameInfo);
}
}
else
{
MessageBox.Show("获取图像失败,错误码:" + nRet);
}
}
public string ReaderImagePath_Temp2;
/// <summary>
/// 抓拍图片 左相机
/// </summary>
public void ReceiveThreadProcess2()
{
MyCamera.MV_FRAME_OUT stFrameInfo = new MyCamera.MV_FRAME_OUT();
int nRet = m_MyCamera2.MV_CC_GetImageBuffer_NET(ref stFrameInfo, 1000);
if (nRet == MyCamera.MV_OK)
{
try
{
int width = stFrameInfo.stFrameInfo.nWidth;
int height = stFrameInfo.stFrameInfo.nHeight;
int stride = width; // Mono8: 每个像素1字节stride = width
// 直接利用内存地址创建 Bitmap 对象
Bitmap bitmap = new Bitmap(width, height, stride, PixelFormat.Format8bppIndexed, stFrameInfo.pBufAddr);
// 设置灰度调色板
ColorPalette palette = bitmap.Palette;
for (int i = 0; i < 256; i++)
{
palette.Entries[i] = Color.FromArgb(i, i, i);
}
bitmap.Palette = palette;
Application.Current.Dispatcher.Invoke(() =>
{
// 转换为 WPF BitmapImage 用于 Image 控件显示
ImageSource2 = Common.BitmapToBitmapSource2(bitmap);
});
ReaderImagePath_Temp2 = AppDomain.CurrentDomain.BaseDirectory + "ImageList\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\" + Guid.NewGuid() + ".jpeg";
//DbHelperSQL.ExecuteSql($"update SCAN_MIAN set img_after='{ReaderImagePath_Temp2}' WHERE id = (SELECT id FROM ( SELECT id FROM SCAN_MIAN ORDER BY id DESC OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY ) AS subquery)");
DbHelperSQL.ExecuteSql($"update SCAN_MIAN set img_after='{ReaderImagePath_Temp2}' WHERE ex = '{ReadImageGuid_Temp[0]}'");
ReadImageGuid_Temp.Remove(ReadImageGuid_Temp[0]);
// 直接将图像数据保存到本地(例如保存为 JPEG 格式)
SaveBitmapToFile(bitmap, ReaderImagePath_Temp2);
bitmap.Dispose();
bitmap = null;
}
catch (Exception ex)
{
MessageBox.Show("图像处理失败: " + ex.Message);
}
finally
{
m_MyCamera.MV_CC_FreeImageBuffer_NET(ref stFrameInfo);
}
}
else
{
MessageBox.Show("获取图像失败,错误码:" + nRet);
}
}
/// <summary>
/// 直接将图像保存为文件
/// </summary>
/// <param name="bitmap"></param>
/// <param name="filePath"></param>
private void SaveBitmapToFile(Bitmap bitmap, string filePath)
{
// 使用 MemoryStream 保存图像,减少内存占用
using (MemoryStream ms = new MemoryStream())
{
bitmap.Save(ms, ImageFormat.Jpeg); // 保存为 JPEG 格式
File.WriteAllBytes(filePath, ms.ToArray());
}
}
/// <summary>
/// 触发拍照指令 右相机
/// </summary>
public void TriggerExec()
{
string sql = @"
IF NOT EXISTS (SELECT 1 FROM SCAN_MIAN WHERE goodcode = @goodcode)
BEGIN
INSERT INTO SCAN_MIAN (time, goodcode, img, img_after, ex)
VALUES (@time, @goodcode, @img, @img_after, @ex)
END";
SqlParameter[] parameters = {
new SqlParameter("@time", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")),
new SqlParameter("@goodcode", "123"),
new SqlParameter("@img", "321"),
new SqlParameter("@img_after", "imgpath_after"),
new SqlParameter("@ex", "guid")
};
DbHelperSQL.ExecuteSql(sql, parameters);
//// ch:触发命令 | en:Trigger command
//int nRet = m_MyCamera.MV_CC_SetCommandValue_NET("TriggerSoftware");
//if (MyCamera.MV_OK != nRet)
//{
// //ShowErrorMsg("Trigger Software Fail!", nRet);
//}
//if (m_bGrabbing)
//{
// Task.Run(() => {
// ReceiveThreadProcess();
// });
//}
//TriggerExec_Left();
}
/// <summary>
/// 触发拍照指令 左相机
/// </summary>
public void TriggerExec_Left()
{
// ch:触发命令 | en:Trigger command
int nRet = m_MyCamera2.MV_CC_SetCommandValue_NET("TriggerSoftware");
if (MyCamera.MV_OK != nRet)
{
//ShowErrorMsg("Trigger Software Fail!", nRet);
}
if (m_bGrabbing2)
{
Task.Run(() => {
ReceiveThreadProcess2();
});
}
}
public static Bitmap CropBarcodeRegion(Bitmap original, Rectangle roi)
{
// 确保裁剪区域在图片范围内
roi.Intersect(new Rectangle(0, 0, original.Width, original.Height));
// 进行裁剪
return original.Clone(roi, original.PixelFormat);
}
#endregion
#region
private SerialPort _serialPort;
private IModbusSerialMaster _modbusMaster;
private DispatcherTimer _timer;
private bool _leftCameraTriggered;
public bool LeftCameraTriggered
{
get => _leftCameraTriggered;
set { _leftCameraTriggered = value; OnPropertyChanged("LeftCameraTriggered"); }
}
private bool _rightCameraTriggered;
public bool RightCameraTriggered
{
get => _rightCameraTriggered;
set { _rightCameraTriggered = value; OnPropertyChanged("RightCameraTriggered"); }
}
/// <summary>
/// 初始化连接PLC
/// </summary>
private void InitializeModbus()
{
try
{
// 创建并打开串口
_serialPort = new SerialPort("COM10", 115200, Parity.None, 8, StopBits.One);
_serialPort.ReadTimeout = 1000; // 设置串口读取超时为1秒
_serialPort.WriteTimeout = 1000; // 设置串口写入超时为1秒
_serialPort.Open();
// 创建 Modbus 主站
_modbusMaster = ModbusSerialMaster.CreateRtu(_serialPort);
_modbusMaster.Transport.Retries = 3;
_modbusMaster.Transport.WaitToRetryMilliseconds = 100;
_modbusMaster.Transport.SlaveBusyUsesRetryCount = true;
Console.WriteLine("MODBUS 连接成功!");
}
catch (Exception ex)
{
Console.WriteLine("MODBUS 连接失败: " + ex.Message);
}
}
/// <summary>
/// 线程 监听接收信息
/// </summary>
private void StartPolling()
{
_timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(500) };
_timer.Tick += (s, e) => ReadPlcData();
_timer.Start();
}
private async Task<ushort[]> ReadHoldingRegistersWithRetry(byte slaveId, ushort startAddress, ushort numRegisters)
{
int retryCount = 3;
int waitTimeMs = 100;
ushort[] result = null;
for (int i = 0; i < retryCount; i++)
{
try
{
result = _modbusMaster.ReadHoldingRegisters(slaveId, startAddress, numRegisters);
return result; // 成功读取则返回结果
}
catch (Exception ex)
{
Console.WriteLine($"Modbus 读取失败: {ex.Message}");
if (i < retryCount - 1) // 如果不是最后一次重试
{
await Task.Delay(waitTimeMs); // 等待一段时间后重试
}
}
}
return result; // 如果超过重试次数还没成功,可以返回 null 或处理失败逻辑
}
/// <summary>
/// 信息接收方法
/// </summary>
private void ReadPlcData()
{
try
{
//return;
byte slaveId = 3; // 站号3
//// 读取左相机寄存器VW2000Modbus地址0
//ushort[] leftCameraRegisters = _modbusMaster.ReadHoldingRegisters(slaveId, 0, 1);
//// 读取右相机寄存器VW2002Modbus地址1
//ushort[] rightCameraRegisters = _modbusMaster.ReadHoldingRegisters(slaveId, 1, 1);
// 一次性读取左相机和右相机的寄存器
ushort[] registers = _modbusMaster.ReadHoldingRegisters(slaveId, 0, 2);
// 解析左相机信号VW2000的第0位
LeftCameraTriggered = (registers[0] & 0x0001) != 0;
// 解析右相机信号VW2002的第1位
RightCameraTriggered = (registers[1] & 0x0002) != 0;
// 触发拍照
if (RightCameraTriggered) TakePhoto("右相机");
if (LeftCameraTriggered) TakePhoto("左相机");
}
catch (Exception ex)
{
Console.WriteLine("PLC 读取失败: " + ex.Message);
}
}
/// <summary>
/// 向上位机Y5/7发送右相机OK信号至PLC
/// </summary>
/// <param name="isActive">true=激活信号false=关闭信号</param>
public void SendRightCameraOkSignal()
{
try
{
byte slaveId = 3; // 站号3
ushort coilAddress = 7; // Y5/7的Modbus VW 2014
short value = 1;
_modbusMaster.WriteSingleRegister(slaveId, coilAddress,(ushort)value);
}
catch (Exception ex)
{
Console.WriteLine("PLC信号发送失败: " + ex.Message);
}
}
private DateTime lastLeftTriggerTime = DateTime.MinValue; // 左相机上次触发时间
private DateTime lastRightTriggerTime = DateTime.MinValue; // 右相机上次触发时间
/// <summary>
/// 获取哪个相机要触发
/// </summary>
/// <param name="camera"></param>
private void TakePhoto(string camera)
{
DateTime currentTime = DateTime.Now;
if (camera == "左相机")
{
// 判断左相机触发的间隔时间
if ((currentTime - lastLeftTriggerTime).TotalSeconds < 1)
{
Console.WriteLine("左相机触发太快,请稍等...");
return;
}
// 更新左相机的触发时间
lastLeftTriggerTime = currentTime;
Console.WriteLine("左相机触发" + currentTime.ToString("yyyy-MM-dd HH:mm:ss"));
TriggerExec_Left();
}
else if (camera == "右相机")
{
// 判断右相机触发的间隔时间
if ((currentTime - lastRightTriggerTime).TotalSeconds < 1)
{
Console.WriteLine("右相机触发太快,请稍等...");
return;
}
// 更新右相机的触发时间
lastRightTriggerTime = currentTime;
Console.WriteLine("右相机触发" + currentTime.ToString("yyyy-MM-dd HH:mm:ss"));
TriggerExec();
}
}
public void Dispose()
{
// 停止定时器
_timer?.Stop();
// 释放 ModbusMaster 和串口资源
_modbusMaster?.Dispose();
if (_serialPort?.IsOpen == true) _serialPort.Close();
}
#endregion
public MianWindowViewModel()
{
GetPossibleRegions();
////ch: 初始化SDK | en: Initialize SDK
//MyCamera.MV_CC_Initialize_NET();
////相机初始化
//OpenCamera();
//plc 通讯初始化
//InitializeModbus();
//StartPolling();
}
/// <summary>
/// 获取裁切范围
/// </summary>
public void GetPossibleRegions()
{
possibleRegions.Clear();
DataView dv = DbHelperSQL.Query("select * from IMAGEINFO").Tables[0].DefaultView;
foreach (DataRowView rowView in dv)
{
possibleRegions.Add(new Rectangle(int.Parse(rowView["x"].ToString()), int.Parse(rowView["y"].ToString()), int.Parse(rowView["width"].ToString()), int.Parse(rowView["height"].ToString())));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}