AHAQ_QGNH/AnQing/ViewModel/MianWindowViewModel.cs
2025-05-19 09:31:47 +08:00

849 lines
32 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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));
}
}
}
}