1048 lines
42 KiB
C#
1048 lines
42 KiB
C#
using Modbus.Device;
|
||
using Sunny.UI;
|
||
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Data;
|
||
using System.Drawing;
|
||
using System.Linq;
|
||
using System.Net;
|
||
using System.Net.NetworkInformation;
|
||
using System.Net.Sockets;
|
||
using System.Runtime.InteropServices;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Forms;
|
||
using System.Windows.Forms.DataVisualization.Charting;
|
||
using 薄膜气体透过率测试仪;
|
||
using 薄膜气体透过率测试仪.Data;
|
||
//新
|
||
namespace NET_HRF4826
|
||
{
|
||
public partial class Form1 : UIForm
|
||
{
|
||
private TcpClient _tcpClient => ModbusResourceManager.Instance.TcpClient;
|
||
private IModbusMaster _modbusMaster => ModbusResourceManager.Instance.ModbusMaster;
|
||
|
||
Function ma;
|
||
DataChange c = new DataChange();
|
||
private System.Windows.Forms.Timer _readtimer;
|
||
System.Windows.Forms.Timer Timer;
|
||
|
||
private int smoothWindow = 100; // 平滑窗口大小,可根据需要调整
|
||
Thread threadReceiveData = new Thread(ReceiveData);
|
||
static int waitbytes = 7000;
|
||
UInt16 temp;
|
||
static bool daq_flag = false;
|
||
static byte[] readdata = new byte[waitbytes];
|
||
static byte[] senddata = new byte[47];
|
||
|
||
public static int GetBit(byte b, int index) { return ((b & (1 << index)) > 0) ? 1 : 0; }
|
||
|
||
private IPAddress serverIP;
|
||
private IPEndPoint serverFullAddr;
|
||
private static Socket sock;
|
||
|
||
private int num = 100;
|
||
private Queue<double> dataQueue0 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue1 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue2 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue3 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue4 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue5 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue6 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue7 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue8 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue9 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue10 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue11 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue12 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue13 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue14 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue15 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue16 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue17 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue18 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue19 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue20 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue21 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue22 = new Queue<double>(1000);
|
||
private Queue<double> dataQueue23 = new Queue<double>(1000);
|
||
|
||
private List<DataRecord> allDataRecords;
|
||
private int sampleCounter = 0;
|
||
private object dataLock = new object();
|
||
private int totalRecordsAdded = 0; // 用于调试
|
||
|
||
// 新增:是否已开始记录有效数据
|
||
private bool hasValidDataStarted = false;
|
||
|
||
|
||
|
||
// 存储从PLC读取的最新压力值(单位Pa)
|
||
private float? _plcLowPressurePa;
|
||
private float? _plcHighPressurePa;
|
||
private readonly object _plcPressureLock = new object();
|
||
|
||
|
||
|
||
public Form1()
|
||
{
|
||
InitializeComponent();
|
||
}
|
||
|
||
|
||
|
||
|
||
private System.Windows.Forms.Timer InitTimer()
|
||
{
|
||
var timer = new System.Windows.Forms.Timer()
|
||
{
|
||
Interval = 80,
|
||
};
|
||
timer.Tick += async (s, e) =>
|
||
{
|
||
if (_modbusMaster != null)
|
||
{
|
||
try
|
||
{
|
||
await ReadDataAsync();
|
||
}
|
||
catch { }
|
||
}
|
||
};
|
||
return timer;
|
||
|
||
}
|
||
|
||
private async System.Threading.Tasks.Task ReadDataAsync()
|
||
{
|
||
try
|
||
{
|
||
if (_modbusMaster == null || _tcpClient == null || !_tcpClient.Connected)
|
||
return;
|
||
|
||
// 高压:使用ReadFloatAsync读取浮点数
|
||
var highPressureTask = ReadFloatAsync(1034, 2);
|
||
|
||
// 低压:使用ReadDWordAsync读取32位整数
|
||
var lowPressureTask = ReadDWordAsync(1140, null); // 低压地址假设为1140,请确认实际地址
|
||
|
||
await Task.WhenAll(highPressureTask, lowPressureTask);
|
||
|
||
// 更新高压字段(单位假设为Pa)
|
||
if (highPressureTask.Result.HasValue)
|
||
{
|
||
lock (_plcPressureLock)
|
||
_plcHighPressurePa = highPressureTask.Result.Value;
|
||
}
|
||
|
||
// 更新低压字段(从DWord读取的整数值,假设单位已经是Pa)
|
||
if (lowPressureTask.Result.HasValue)
|
||
{
|
||
lock (_plcPressureLock)
|
||
_plcLowPressurePa = lowPressureTask.Result.Value; // 直接使用整数值
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_readtimer?.Stop();
|
||
Timer?.Stop();
|
||
System.Diagnostics.Debug.WriteLine($"读取数据失败:{ex.Message}");
|
||
}
|
||
}
|
||
|
||
private async Task<float?> ReadFloatAsync(int address, int length)
|
||
{
|
||
try
|
||
{
|
||
ushort[] registers = await Task.Run(async () =>
|
||
{
|
||
if (_modbusMaster == null) return null;
|
||
return await _modbusMaster.ReadHoldingRegistersAsync(1, (ushort)address, (ushort)length);
|
||
});
|
||
if (registers != null && registers.Length >= 2)
|
||
{
|
||
return c.UshortToFloat(registers[1], registers[0]);
|
||
}
|
||
}
|
||
catch { }
|
||
return null;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
private async Task<int?> ReadDWordAsync(int startAddress, Label control, string format = "D", string unit = "")
|
||
{
|
||
try
|
||
{
|
||
// 32位 DWord 占用 2 个连续寄存器
|
||
ushort[] registers = await Task.Run(async () =>
|
||
{
|
||
if (_modbusMaster == null) return null;
|
||
return await _modbusMaster.ReadHoldingRegistersAsync(1, (ushort)startAddress, 2);
|
||
});
|
||
|
||
if (registers != null && registers.Length >= 2)
|
||
{
|
||
// 将两个16位寄存器组合成32位有符号整数
|
||
// 注意寄存器顺序:通常是 低16位在前(D1140),高16位在后(D1141)
|
||
int value = (registers[1] << 16) | registers[0];
|
||
|
||
// 如果提供了control,则更新UI
|
||
if (control != null && !control.IsDisposed)
|
||
{
|
||
control.Invoke(new Action(() =>
|
||
{
|
||
control.Text = value.ToString() + unit;
|
||
}));
|
||
}
|
||
|
||
return value;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"读取DWord地址{startAddress}失败:{ex.Message}");
|
||
}
|
||
return null;
|
||
}
|
||
|
||
private void Read_Data()
|
||
{
|
||
// ★ 关键修复:复制 readdata 到本地缓冲区,避免被后台线程修改
|
||
byte[] localBuffer = new byte[waitbytes];
|
||
lock (readdata) // 与 ReceiveData 中的写入保持同步
|
||
{
|
||
Buffer.BlockCopy(readdata, 0, localBuffer, 0, waitbytes);
|
||
}
|
||
|
||
byte[,] dat = new byte[num, 70];
|
||
float[,] volt = new float[num, 16];
|
||
byte DI0, DI1, DI2, DI3;
|
||
|
||
// 队列处理(保持原样)
|
||
if (dataQueue1.Count > num)
|
||
{
|
||
for (int i = 0; i < num; i++)
|
||
{
|
||
dataQueue0.Dequeue();
|
||
dataQueue1.Dequeue();
|
||
dataQueue2.Dequeue();
|
||
dataQueue3.Dequeue();
|
||
dataQueue4.Dequeue();
|
||
dataQueue5.Dequeue();
|
||
dataQueue6.Dequeue();
|
||
dataQueue7.Dequeue();
|
||
dataQueue8.Dequeue();
|
||
dataQueue9.Dequeue();
|
||
dataQueue10.Dequeue();
|
||
dataQueue11.Dequeue();
|
||
dataQueue12.Dequeue();
|
||
dataQueue13.Dequeue();
|
||
dataQueue14.Dequeue();
|
||
dataQueue15.Dequeue();
|
||
}
|
||
this.chart1.ChartAreas[0].AxisX.Minimum += num;
|
||
this.chart1.ChartAreas[0].AxisX.Maximum += num;
|
||
}
|
||
|
||
// 循环处理每个采样点
|
||
for (int i = 0; i < num; i++)
|
||
{
|
||
// 使用本地缓冲区填充 dat
|
||
for (int j = 0; j < 70; j++)
|
||
{
|
||
dat[i, j] = localBuffer[i * 70 + j];
|
||
}
|
||
|
||
// 计算AI电压
|
||
for (int j = 0; j < 16; j++)
|
||
{
|
||
if (AI_Range_comboBox.SelectedIndex > 0)
|
||
{
|
||
volt[i, j] = dat[i, j + j] * 256 + dat[i, j + j + 1];
|
||
volt[i, j] = volt[i, j] > 32767 ? ((volt[i, j] - 65535) / 32767 * 10) : volt[i, j] / 32767 * 10;
|
||
}
|
||
else
|
||
{
|
||
volt[i, j] = dat[i, j + j] * 256 + dat[i, j + j + 1];
|
||
volt[i, j] = volt[i, j] > 32767 ? ((volt[i, j] - 65535) / 32767 * 5) : volt[i, j] / 32767 * 5;
|
||
}
|
||
}
|
||
|
||
// 入队绘图数据
|
||
dataQueue0.Enqueue(volt[i, 0]);
|
||
dataQueue1.Enqueue(volt[i, 1]);
|
||
dataQueue2.Enqueue(volt[i, 2]);
|
||
dataQueue3.Enqueue(volt[i, 3]);
|
||
dataQueue4.Enqueue(volt[i, 4]);
|
||
dataQueue5.Enqueue(volt[i, 5]);
|
||
dataQueue6.Enqueue(volt[i, 6]);
|
||
dataQueue7.Enqueue(volt[i, 7]);
|
||
dataQueue8.Enqueue(volt[i, 8]);
|
||
dataQueue9.Enqueue(volt[i, 9]);
|
||
dataQueue10.Enqueue(volt[i, 10]);
|
||
dataQueue11.Enqueue(volt[i, 11]);
|
||
dataQueue12.Enqueue(volt[i, 12]);
|
||
dataQueue13.Enqueue(volt[i, 13]);
|
||
dataQueue14.Enqueue(volt[i, 14]);
|
||
dataQueue15.Enqueue(volt[i, 15]);
|
||
|
||
// 计算脉冲和频率
|
||
UInt32 pulseCount0 = Convert.ToUInt32((dat[i, 51] << 24) + (dat[i, 50] << 16) + (dat[i, 49] << 8) + dat[i, 48]);
|
||
UInt32 pulseCount1 = Convert.ToUInt32((dat[i, 55] << 24) + (dat[i, 54] << 16) + (dat[i, 53] << 8) + dat[i, 52]);
|
||
UInt32 pulseCount2 = Convert.ToUInt32((dat[i, 59] << 24) + (dat[i, 58] << 16) + (dat[i, 57] << 8) + dat[i, 56]);
|
||
|
||
UInt32 frequency0 = Convert.ToUInt32((dat[i, 62] << 16) + (dat[i, 61] << 8) + dat[i, 60]);
|
||
UInt32 frequency1 = Convert.ToUInt32((dat[i, 65] << 16) + (dat[i, 64] << 8) + dat[i, 63]);
|
||
UInt32 frequency2 = Convert.ToUInt32((dat[i, 68] << 16) + (dat[i, 67] << 8) + dat[i, 66]);
|
||
|
||
|
||
|
||
|
||
// ---------- 新增:判断是否应记录当前点 ----------
|
||
bool shouldRecord = hasValidDataStarted
|
||
|| Math.Abs(volt[i, 0]) > 0.001
|
||
|| Math.Abs(volt[i, 1]) > 0.001;
|
||
if (shouldRecord)
|
||
{
|
||
if (!hasValidDataStarted) hasValidDataStarted = true;
|
||
|
||
var record = new DataRecord
|
||
{
|
||
Timestamp = DateTime.Now,
|
||
SampleIndex = sampleCounter++,
|
||
AI = new float[] { volt[i,0], volt[i,1], volt[i,2], volt[i,3],
|
||
volt[i,4], volt[i,5], volt[i,6], volt[i,7],
|
||
volt[i,8], volt[i,9], volt[i,10], volt[i,11],
|
||
volt[i,12], volt[i,13], volt[i,14], volt[i,15] },
|
||
DI = new byte[] { dat[i, 32], dat[i, 33], dat[i, 34], dat[i, 35] },
|
||
PulseCount = new uint[] { pulseCount0, pulseCount1, pulseCount2 },
|
||
Frequency = new uint[] { frequency0, frequency1, frequency2 }
|
||
};
|
||
|
||
// 从PLC字段获取当前压力值(单位Pa)
|
||
lock (_plcPressureLock)
|
||
{
|
||
record.LowPressureCalibrated = _plcLowPressurePa.HasValue ? (float)_plcLowPressurePa.Value : 0f;
|
||
record.HighPressureCalibrated = _plcHighPressurePa ?? 0f;
|
||
}
|
||
|
||
lock (dataLock)
|
||
{
|
||
allDataRecords.Add(record);
|
||
totalRecordsAdded++;
|
||
if (totalRecordsAdded % 1000 == 0)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"已添加 {totalRecordsAdded} 条记录,AI0电压={volt[i, 0]:F4}V");
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
// 入队绘图数据(无论是否记录都执行,保证波形显示正常)
|
||
dataQueue0.Enqueue(volt[i, 0]);
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
//// 更新UI(使用第一个采样点的数据)
|
||
//// AI0 压力转换(0 ~ 100 kPa)
|
||
//double pressure0 = volt[0, 0] * 10.0;
|
||
//AI0_textBox.Text = pressure0.ToString("f3") + " kPa";
|
||
|
||
//// AI1 压力转换(0 ~ 600 kPa)
|
||
//double pressure1 = volt[0, 1] * 60.0;
|
||
//AI1_textBox.Text = pressure1.ToString("f3") + " kPa";
|
||
|
||
|
||
lock (_plcPressureLock)
|
||
{
|
||
if (_plcLowPressurePa.HasValue)
|
||
AI0_textBox.Text = _plcLowPressurePa.Value.ToString("F1") + " Pa";
|
||
if (_plcHighPressurePa.HasValue)
|
||
AI1_textBox.Text = _plcHighPressurePa.Value.ToString("F1") + " kPa";
|
||
}
|
||
|
||
// 更新DI按钮状态(使用第一个采样点)
|
||
DI0 = dat[0, 32];
|
||
DI1 = dat[0, 33];
|
||
DI2 = dat[0, 34];
|
||
DI3 = dat[0, 35];
|
||
|
||
|
||
// 更新脉冲计数和频率文本框
|
||
PulseCount0_textBox.Text = Convert.ToUInt32((dat[0, 51] << 24) + (dat[0, 50] << 16) + (dat[0, 49] << 8) + dat[0, 48]).ToString();
|
||
PulseCount1_textBox.Text = Convert.ToUInt32((dat[0, 55] << 24) + (dat[0, 54] << 16) + (dat[0, 53] << 8) + dat[0, 52]).ToString();
|
||
PulseCount2_textBox.Text = Convert.ToUInt32((dat[0, 59] << 24) + (dat[0, 58] << 16) + (dat[0, 57] << 8) + dat[0, 56]).ToString();
|
||
|
||
Frequency0_textBox.Text = Convert.ToUInt32((dat[0, 62] << 16) + (dat[0, 61] << 8) + dat[0, 60]).ToString();
|
||
Frequency1_textBox.Text = Convert.ToUInt32((dat[0, 65] << 16) + (dat[0, 64] << 8) + dat[0, 63]).ToString();
|
||
Frequency2_textBox.Text = Convert.ToUInt32((dat[0, 68] << 16) + (dat[0, 67] << 8) + dat[0, 66]).ToString();
|
||
}
|
||
|
||
private void btnStop_Click(object sender, EventArgs e)
|
||
{
|
||
senddata[38] = 0xBB;
|
||
daq_flag = false;
|
||
if (sock.Connected)
|
||
sock.Send(senddata);
|
||
sock.Close();
|
||
timerPaint.Enabled = false;
|
||
btnStart.Enabled = true;
|
||
}
|
||
|
||
private void Write_Data()
|
||
{
|
||
UInt16 temp = 0, div = 72;
|
||
|
||
senddata[0] = (byte)((btnDO7.ImageIndex << 7) + (btnDO6.ImageIndex << 6) + (btnDO5.ImageIndex << 5) + (btnDO4.ImageIndex << 4) + (btnDO3.ImageIndex << 3) + (btnDO2.ImageIndex << 2) + (btnDO1.ImageIndex << 1) + btnDO0.ImageIndex);
|
||
senddata[1] = (byte)((btnDO15.ImageIndex << 7) + (btnDO14.ImageIndex << 6) + (btnDO13.ImageIndex << 5) + (btnDO12.ImageIndex << 4) + (btnDO11.ImageIndex << 3) + (btnDO10.ImageIndex << 2) + (btnDO9.ImageIndex << 1) + btnDO8.ImageIndex);
|
||
|
||
// 新增的16个DO通道
|
||
senddata[2] = (byte)((btnDO23.ImageIndex << 7) + (btnDO22.ImageIndex << 6) + (btnDO21.ImageIndex << 5) + (btnDO20.ImageIndex << 4) + (btnDO19.ImageIndex << 3) + (btnDO18.ImageIndex << 2) + (btnDO17.ImageIndex << 1) + btnDO16.ImageIndex);
|
||
senddata[3] = (byte)((btnDO31.ImageIndex << 7) + (btnDO30.ImageIndex << 6) + (btnDO29.ImageIndex << 5) + (btnDO28.ImageIndex << 4) + (btnDO27.ImageIndex << 3) + (btnDO26.ImageIndex << 2) + (btnDO25.ImageIndex << 1) + btnDO24.ImageIndex);
|
||
|
||
temp = Convert.ToUInt16(Convert.ToSingle(AO0_textBox.Text) * 4095 / 10);
|
||
senddata[4] = Convert.ToByte(temp / 256);
|
||
senddata[5] = Convert.ToByte(temp % 256);
|
||
|
||
temp = Convert.ToUInt16(Convert.ToSingle(AO1_textBox.Text) * 4095 / 10);
|
||
senddata[6] = Convert.ToByte(temp / 256);
|
||
senddata[7] = Convert.ToByte(temp % 256);
|
||
|
||
temp = Convert.ToUInt16(72000000 / div / (Convert.ToUInt32(T0_Freq_textBox.Text)));
|
||
senddata[8] = Convert.ToByte(temp / 256);
|
||
senddata[9] = Convert.ToByte(temp % 256);
|
||
senddata[10] = Convert.ToByte(div / 256);
|
||
senddata[11] = Convert.ToByte(div % 256);
|
||
temp = Convert.ToUInt16(temp * (Convert.ToSingle(T0_Duty_textBox.Text)) / 100);
|
||
senddata[12] = Convert.ToByte(temp / 256);
|
||
senddata[13] = Convert.ToByte(temp % 256);
|
||
|
||
temp = Convert.ToUInt16(72000000 / div / (Convert.ToUInt32(T1_Freq_textBox.Text)));
|
||
senddata[14] = Convert.ToByte(temp / 256);
|
||
senddata[15] = Convert.ToByte(temp % 256);
|
||
senddata[16] = Convert.ToByte(div / 256);
|
||
senddata[17] = Convert.ToByte(div % 256);
|
||
temp = Convert.ToUInt16(temp * (Convert.ToSingle(T1_Duty_textBox.Text)) / 100);
|
||
senddata[18] = Convert.ToByte(temp / 256);
|
||
senddata[19] = Convert.ToByte(temp % 256);
|
||
|
||
temp = Convert.ToUInt16(72000000 / div / (Convert.ToUInt32(T2_Freq_textBox.Text)));
|
||
senddata[20] = Convert.ToByte(temp / 256);
|
||
senddata[21] = Convert.ToByte(temp % 256);
|
||
senddata[22] = Convert.ToByte(div / 256);
|
||
senddata[23] = Convert.ToByte(div % 256);
|
||
temp = Convert.ToUInt16(temp * (Convert.ToSingle(T2_Duty_textBox.Text)) / 100);
|
||
senddata[24] = Convert.ToByte(temp / 256);
|
||
senddata[25] = Convert.ToByte(temp % 256);
|
||
|
||
//temp = Convert.ToUInt16(72000000 / div / (Convert.ToUInt32(T3_Freq_textBox.Text)));
|
||
senddata[26] = Convert.ToByte(temp / 256);
|
||
senddata[27] = Convert.ToByte(temp % 256);
|
||
senddata[28] = Convert.ToByte(div / 256);
|
||
senddata[29] = Convert.ToByte(div % 256);
|
||
//temp = Convert.ToUInt16(temp * (Convert.ToSingle(T3_Duty_textBox.Text)) / 100);
|
||
senddata[30] = Convert.ToByte(temp / 256);
|
||
senddata[31] = Convert.ToByte(temp % 256);
|
||
|
||
senddata[38] = 0x55; //输出使能指令,初始化已写,此处可删
|
||
sock.Send(senddata);
|
||
}
|
||
|
||
private void btnStart_Click(object sender, EventArgs e)
|
||
{
|
||
// 清空之前的数据
|
||
lock (dataLock)
|
||
{
|
||
allDataRecords = new List<DataRecord>();
|
||
hasValidDataStarted = false; // ← 新增
|
||
}
|
||
sampleCounter = 0;
|
||
|
||
btnStart.Enabled = false;
|
||
btnStop.Enabled = true;
|
||
|
||
serverIP = IPAddress.Parse(tbxIP.Text);
|
||
Ping pingSend = new Ping();
|
||
PingReply reply = pingSend.Send(serverIP, 1000);
|
||
if (reply.Status == IPStatus.Success)
|
||
{
|
||
try
|
||
{
|
||
serverFullAddr = new IPEndPoint(serverIP, int.Parse(tbxPort.Text));
|
||
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||
sock.Connect(serverFullAddr);
|
||
TCP_flag.ImageIndex = 1;
|
||
lblError.Text = "连接采集卡成功!";
|
||
timerPaint.Enabled = true;
|
||
|
||
senddata[38] = 0x55;
|
||
senddata[39] = (byte)AI_Range_comboBox.SelectedIndex;
|
||
temp = Convert.ToUInt16(Convert.ToSingle(DAQfre_textBox.Text));
|
||
senddata[40] = Convert.ToByte(temp / 256);
|
||
senddata[41] = Convert.ToByte(temp % 256);
|
||
senddata[42] = 3;
|
||
|
||
sock.Send(senddata);
|
||
|
||
daq_flag = true;
|
||
timerPaint.Interval = 10;
|
||
timerPaint.Enabled = true;
|
||
}
|
||
catch (Exception ee)
|
||
{
|
||
TCP_flag.ImageIndex = 0;
|
||
lblError.Text = "连接采集卡失败……请检查采集卡IP设置" + ee;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
btnDO0.ImageIndex = 0;
|
||
lblError.Text = "Ping不通……请确保采集卡已连接并与电脑IP在同一网段";
|
||
}
|
||
}
|
||
private void btnExit_Click(object sender, EventArgs e)
|
||
{
|
||
senddata[38] = 0xBB;
|
||
if (sock.Connected)
|
||
sock.Send(senddata);
|
||
sock.Close();
|
||
btnStart.Enabled = true;
|
||
System.Environment.Exit(0);
|
||
}
|
||
|
||
private void timerPaint_Tick_1(object sender, EventArgs e)
|
||
{
|
||
Write_Data();
|
||
Read_Data();
|
||
}
|
||
|
||
static void ReceiveData()
|
||
{
|
||
while (true)
|
||
{
|
||
if (daq_flag && sock.Available >= waitbytes)
|
||
{
|
||
byte[] tempBuffer = new byte[waitbytes];
|
||
int received = sock.Receive(tempBuffer, waitbytes, 0);
|
||
if (received == waitbytes)
|
||
{
|
||
lock (readdata)
|
||
{
|
||
Buffer.BlockCopy(tempBuffer, 0, readdata, 0, waitbytes);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void btnDO_Click(object sender, EventArgs e)
|
||
{
|
||
Button btn = (sender as Button);
|
||
bool isopen = (btn.ImageIndex == 0) ? false : true;
|
||
|
||
btn.ImageIndex = (isopen) ? 0 : 1;
|
||
}
|
||
|
||
private void btnOpenAll_Click(object sender, EventArgs e)
|
||
{
|
||
btnDO0.ImageIndex = 1;
|
||
btnDO1.ImageIndex = 1;
|
||
btnDO2.ImageIndex = 1;
|
||
btnDO3.ImageIndex = 1;
|
||
btnDO4.ImageIndex = 1;
|
||
btnDO5.ImageIndex = 1;
|
||
btnDO6.ImageIndex = 1;
|
||
btnDO7.ImageIndex = 1;
|
||
btnDO8.ImageIndex = 1;
|
||
btnDO9.ImageIndex = 1;
|
||
btnDO10.ImageIndex = 1;
|
||
btnDO11.ImageIndex = 1;
|
||
btnDO12.ImageIndex = 1;
|
||
btnDO13.ImageIndex = 1;
|
||
btnDO14.ImageIndex = 1;
|
||
btnDO15.ImageIndex = 1;
|
||
btnDO16.ImageIndex = 1;
|
||
btnDO17.ImageIndex = 1;
|
||
btnDO18.ImageIndex = 1;
|
||
btnDO19.ImageIndex = 1;
|
||
btnDO20.ImageIndex = 1;
|
||
btnDO21.ImageIndex = 1;
|
||
btnDO22.ImageIndex = 1;
|
||
btnDO23.ImageIndex = 1;
|
||
btnDO24.ImageIndex = 1;
|
||
btnDO25.ImageIndex = 1;
|
||
btnDO26.ImageIndex = 1;
|
||
btnDO27.ImageIndex = 1;
|
||
btnDO28.ImageIndex = 1;
|
||
btnDO29.ImageIndex = 1;
|
||
btnDO30.ImageIndex = 1;
|
||
btnDO31.ImageIndex = 1;
|
||
}
|
||
|
||
private void btnCloseAll_Click(object sender, EventArgs e)
|
||
{
|
||
btnDO0.ImageIndex = 0;
|
||
btnDO1.ImageIndex = 0;
|
||
btnDO2.ImageIndex = 0;
|
||
btnDO3.ImageIndex = 0;
|
||
btnDO4.ImageIndex = 0;
|
||
btnDO5.ImageIndex = 0;
|
||
btnDO6.ImageIndex = 0;
|
||
btnDO7.ImageIndex = 0;
|
||
btnDO8.ImageIndex = 0;
|
||
btnDO9.ImageIndex = 0;
|
||
btnDO10.ImageIndex = 0;
|
||
btnDO11.ImageIndex = 0;
|
||
btnDO12.ImageIndex = 0;
|
||
btnDO13.ImageIndex = 0;
|
||
btnDO14.ImageIndex = 0;
|
||
btnDO15.ImageIndex = 0;
|
||
btnDO16.ImageIndex = 0;
|
||
btnDO17.ImageIndex = 0;
|
||
btnDO18.ImageIndex = 0;
|
||
btnDO19.ImageIndex = 0;
|
||
btnDO20.ImageIndex = 0;
|
||
btnDO21.ImageIndex = 0;
|
||
btnDO22.ImageIndex = 0;
|
||
btnDO23.ImageIndex = 0;
|
||
btnDO24.ImageIndex = 0;
|
||
btnDO25.ImageIndex = 0;
|
||
btnDO26.ImageIndex = 0;
|
||
btnDO27.ImageIndex = 0;
|
||
btnDO28.ImageIndex = 0;
|
||
btnDO29.ImageIndex = 0;
|
||
btnDO30.ImageIndex = 0;
|
||
btnDO31.ImageIndex = 0;
|
||
}
|
||
|
||
private void InitChart()
|
||
{
|
||
//定义图表区域
|
||
this.chart1.ChartAreas.Clear();
|
||
ChartArea chartArea1 = new ChartArea("C0");
|
||
this.chart1.ChartAreas.Add(chartArea1);
|
||
this.chart1.BackColor = Color.WhiteSmoke;
|
||
|
||
//定义存储和显示点的容器
|
||
this.chart1.Series.Clear();
|
||
Series series0 = new Series("S0"); //定义24条曲线,分别显示8个通道电压
|
||
Series series1 = new Series("S1");
|
||
Series series2 = new Series("S2");
|
||
Series series3 = new Series("S3");
|
||
Series series4 = new Series("S4");
|
||
Series series5 = new Series("S5");
|
||
Series series6 = new Series("S6");
|
||
Series series7 = new Series("S7");
|
||
Series series8 = new Series("S8");
|
||
Series series9 = new Series("S9");
|
||
Series series10 = new Series("S10");
|
||
Series series11 = new Series("S11");
|
||
Series series12 = new Series("S12");
|
||
Series series13 = new Series("S13");
|
||
Series series14 = new Series("S14");
|
||
Series series15 = new Series("S15");
|
||
Series series16 = new Series("S16");
|
||
Series series17 = new Series("S17");
|
||
Series series18 = new Series("S18");
|
||
Series series19 = new Series("S19");
|
||
Series series20 = new Series("S20");
|
||
Series series21 = new Series("S21");
|
||
Series series22 = new Series("S22");
|
||
Series series23 = new Series("S23");
|
||
|
||
series0.ChartArea = "C0";
|
||
this.chart1.Series.Add(series0);
|
||
this.chart1.Series.Add(series1);
|
||
this.chart1.Series.Add(series2);
|
||
this.chart1.Series.Add(series3);
|
||
this.chart1.Series.Add(series4);
|
||
this.chart1.Series.Add(series5);
|
||
this.chart1.Series.Add(series6);
|
||
this.chart1.Series.Add(series7);
|
||
this.chart1.Series.Add(series8);
|
||
this.chart1.Series.Add(series9);
|
||
this.chart1.Series.Add(series10);
|
||
this.chart1.Series.Add(series11);
|
||
this.chart1.Series.Add(series12);
|
||
this.chart1.Series.Add(series13);
|
||
this.chart1.Series.Add(series14);
|
||
this.chart1.Series.Add(series15);
|
||
this.chart1.Series.Add(series16);
|
||
this.chart1.Series.Add(series17);
|
||
this.chart1.Series.Add(series18);
|
||
this.chart1.Series.Add(series19);
|
||
this.chart1.Series.Add(series20);
|
||
this.chart1.Series.Add(series21);
|
||
this.chart1.Series.Add(series22);
|
||
this.chart1.Series.Add(series23);
|
||
|
||
//设置图表显示样式
|
||
this.chart1.ChartAreas[0].AxisX.Minimum = 0;
|
||
this.chart1.ChartAreas[0].AxisX.Maximum = 200;
|
||
this.chart1.ChartAreas[0].AxisX.Interval = 20;
|
||
this.chart1.ChartAreas[0].AxisY.Minimum = -10;
|
||
this.chart1.ChartAreas[0].AxisY.Maximum = 10;
|
||
this.chart1.ChartAreas[0].AxisY.Interval = 5;
|
||
this.chart1.ChartAreas[0].AxisX.MajorGrid.LineColor = System.Drawing.Color.Silver;
|
||
this.chart1.ChartAreas[0].AxisY.MajorGrid.LineColor = System.Drawing.Color.Silver;
|
||
|
||
//设置图表曲线颜色和显示样式
|
||
this.chart1.Series[0].Color = Color.Red;
|
||
this.chart1.Series[1].Color = Color.Black;
|
||
this.chart1.Series[2].Color = Color.Orange;
|
||
this.chart1.Series[3].Color = Color.MediumVioletRed;
|
||
this.chart1.Series[4].Color = Color.Blue;
|
||
this.chart1.Series[5].Color = Color.Green;
|
||
this.chart1.Series[6].Color = Color.Sienna;
|
||
this.chart1.Series[7].Color = Color.Teal;
|
||
this.chart1.Series[8].Color = Color.Red;
|
||
this.chart1.Series[9].Color = Color.Black;
|
||
this.chart1.Series[10].Color = Color.Orange;
|
||
this.chart1.Series[11].Color = Color.MediumVioletRed;
|
||
this.chart1.Series[12].Color = Color.Blue;
|
||
this.chart1.Series[13].Color = Color.Green;
|
||
this.chart1.Series[14].Color = Color.Sienna;
|
||
this.chart1.Series[15].Color = Color.Teal;
|
||
this.chart1.Series[16].Color = Color.Red;
|
||
this.chart1.Series[17].Color = Color.Black;
|
||
this.chart1.Series[18].Color = Color.Orange;
|
||
this.chart1.Series[19].Color = Color.MediumVioletRed;
|
||
this.chart1.Series[20].Color = Color.Blue;
|
||
this.chart1.Series[21].Color = Color.Green;
|
||
this.chart1.Series[22].Color = Color.Sienna;
|
||
this.chart1.Series[23].Color = Color.Teal;
|
||
for (int i = 0; i < 24; i++)
|
||
{
|
||
this.chart1.Series[i].ChartType = SeriesChartType.FastLine;
|
||
this.chart1.Series[i].Points.Clear();
|
||
}
|
||
this.chart1.Series[0].Points.AddXY(0, 0);
|
||
}
|
||
|
||
private void Init_AO()
|
||
{
|
||
AO0_textBox.Text = Convert.ToDouble(0).ToString("f3");
|
||
AO1_textBox.Text = Convert.ToDouble(0).ToString("f3");
|
||
}
|
||
|
||
private void Init_DAQ_set()
|
||
{
|
||
AI_Range_comboBox.SelectedIndex = 1;
|
||
DAQfre_textBox.Text = Convert.ToString(1000);
|
||
}
|
||
|
||
private void Init_PWM()
|
||
{
|
||
T0_Freq_textBox.Text = Convert.ToString(1000);
|
||
T0_Duty_textBox.Text = Convert.ToString(50);
|
||
T1_Freq_textBox.Text = Convert.ToString(1000);
|
||
T1_Duty_textBox.Text = Convert.ToString(50);
|
||
T2_Freq_textBox.Text = Convert.ToString(1000);
|
||
T2_Duty_textBox.Text = Convert.ToString(50);
|
||
// T3_Freq_textBox.Text = Convert.ToString(1000);
|
||
// T3_Duty_textBox.Text = Convert.ToString(50);
|
||
|
||
Encoder0_textBox.Text = Convert.ToString(0);
|
||
Encoder1_textBox.Text = Convert.ToString(0);
|
||
|
||
|
||
PulseCount0_textBox.Text = Convert.ToString(0);
|
||
PulseCount1_textBox.Text = Convert.ToString(0);
|
||
PulseCount2_textBox.Text = Convert.ToString(0);
|
||
Frequency0_textBox.Text = Convert.ToString(0);
|
||
Frequency1_textBox.Text = Convert.ToString(0);
|
||
Frequency2_textBox.Text = Convert.ToString(0);
|
||
}
|
||
|
||
private void Init_checkBox()
|
||
{
|
||
AI0_checkBox.Checked = true;
|
||
AI1_checkBox.Checked = true;
|
||
AI2_checkBox.Checked = true;
|
||
AI3_checkBox.Checked = true;
|
||
AI4_checkBox.Checked = true;
|
||
AI5_checkBox.Checked = true;
|
||
AI6_checkBox.Checked = true;
|
||
AI7_checkBox.Checked = true;
|
||
AI8_checkBox.Checked = true;
|
||
AI9_checkBox.Checked = true;
|
||
AI10_checkBox.Checked = true;
|
||
AI11_checkBox.Checked = true;
|
||
AI12_checkBox.Checked = true;
|
||
AI13_checkBox.Checked = true;
|
||
AI14_checkBox.Checked = true;
|
||
AI15_checkBox.Checked = true;
|
||
}
|
||
|
||
private void btnClear_Click(object sender, EventArgs e)
|
||
{
|
||
senddata[42] |= (byte)(1 << 0); //编码器0清零
|
||
if (sock.Connected)
|
||
sock.Send(senddata);
|
||
senddata[42] = 0;
|
||
Encoder0_textBox.Text = "0";
|
||
|
||
}
|
||
|
||
private void btnClearEn1_Click(object sender, EventArgs e)
|
||
{
|
||
senddata[42] |= (byte)(1 << 1); //编码器1清零
|
||
if (sock.Connected)
|
||
sock.Send(senddata);
|
||
senddata[42] = 0;
|
||
Encoder1_textBox.Text = "0";
|
||
}
|
||
|
||
private void btnClearCounter0_Click(object sender, EventArgs e)
|
||
{
|
||
// 设置字节42的bit2为1,清零计数0
|
||
senddata[42] |= (byte)(1 << 2);
|
||
|
||
// 发送数据
|
||
if (sock.Connected)
|
||
sock.Send(senddata);
|
||
senddata[42] = 0;
|
||
|
||
// 重置计数0文本框
|
||
PulseCount0_textBox.Text = "0";
|
||
}
|
||
|
||
private void btnClearCounter1_Click(object sender, EventArgs e)
|
||
{
|
||
// 设置字节42的bit3为1,清零计数1
|
||
senddata[42] |= (byte)(1 << 3);
|
||
|
||
// 发送数据
|
||
if (sock.Connected)
|
||
sock.Send(senddata);
|
||
senddata[42] = 0;
|
||
|
||
// 重置计数1文本框
|
||
PulseCount1_textBox.Text = "0";
|
||
}
|
||
|
||
private void btnClearCounter2_Click(object sender, EventArgs e)
|
||
{
|
||
// 设置字节42的bit4为1,清零计数2
|
||
senddata[42] |= (byte)(1 << 4);
|
||
|
||
// 发送数据
|
||
if (sock.Connected)
|
||
sock.Send(senddata);
|
||
senddata[42] = 0;
|
||
|
||
// 重置计数2文本框
|
||
PulseCount2_textBox.Text = "0";
|
||
}
|
||
|
||
|
||
private void Form1_Load(object sender, EventArgs e)
|
||
{
|
||
Init_DAQ_set();
|
||
Init_AO();
|
||
Init_PWM();
|
||
Init_checkBox();
|
||
InitChart();
|
||
threadReceiveData.IsBackground = true; //后台运行
|
||
threadReceiveData.Start(); //采集线程开始
|
||
|
||
this.Resize += new EventHandler(Form1_Resize);//窗体调整大小时引发事件
|
||
X = this.Width;//获取窗体的宽度
|
||
Y = this.Height;//获取窗体的高度
|
||
setTag(this);//调用方法
|
||
|
||
|
||
|
||
string plcIp = "192.168.1.10";
|
||
//string plcIp = "127.0.0.1";
|
||
bool initSuccess = ModbusResourceManager.Instance.Init(plcIp, 502);
|
||
if (!initSuccess)
|
||
{
|
||
MessageBox.Show("连接Modbus服务器失败!", "错误");
|
||
this.Close();
|
||
return;
|
||
}
|
||
|
||
// 检查连接状态
|
||
if (_tcpClient == null || !_tcpClient.Connected)
|
||
{
|
||
MessageBox.Show("Modbus连接异常!", "错误");
|
||
this.Close();
|
||
return;
|
||
}
|
||
ma = new Function(_modbusMaster);
|
||
|
||
_readtimer = InitTimer();
|
||
if (_modbusMaster != null)
|
||
{
|
||
_readtimer.Start();
|
||
}
|
||
|
||
|
||
|
||
|
||
}
|
||
|
||
private float X;//当前窗体的宽度
|
||
private float Y;//当前窗体的高度
|
||
|
||
//将控件的宽,高,左边距,顶边距和字体大小暂存到tag属性中
|
||
private void setTag(Control cons)
|
||
{
|
||
foreach (Control con in cons.Controls)
|
||
{
|
||
con.Tag = con.Width + ":" + con.Height + ":" + con.Left + ":" + con.Top + ":" + con.Font.Size;
|
||
if (con.Controls.Count > 0)
|
||
setTag(con);
|
||
}
|
||
}
|
||
|
||
//根据窗体大小调整控件大小
|
||
private void setControls(float newx, float newy, Control cons)
|
||
{
|
||
//遍历窗体中的控件,重新设置控件的值
|
||
foreach (Control con in cons.Controls)
|
||
{
|
||
string[] mytag = con.Tag.ToString().Split(new char[] { ':' });//获取控件的Tag属性值,并分割后存储字符串数组
|
||
float a = System.Convert.ToSingle(mytag[0]) * newx;//根据窗体缩放比例确定控件的值,宽度
|
||
con.Width = (int)a;//宽度
|
||
a = System.Convert.ToSingle(mytag[1]) * newy;//高度
|
||
con.Height = (int)(a);
|
||
a = System.Convert.ToSingle(mytag[2]) * newx;//左边距离
|
||
con.Left = (int)(a);
|
||
a = System.Convert.ToSingle(mytag[3]) * newy;//上边缘距离
|
||
con.Top = (int)(a);
|
||
Single currentSize = System.Convert.ToSingle(mytag[4]) * newy;//字体大小
|
||
con.Font = new Font(con.Font.Name, currentSize, con.Font.Style, con.Font.Unit);
|
||
if (con.Controls.Count > 0)
|
||
{
|
||
setControls(newx, newy, con);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void Form1_Resize(object sender, EventArgs e)
|
||
{
|
||
float newx = (this.Width) / X; //窗体宽度缩放比例
|
||
float newy = (this.Height) / Y; //窗体高度缩放比例
|
||
setControls(newx, newy, this); //随窗体改变控件大小
|
||
}
|
||
|
||
private void button1_Click(object sender, EventArgs e)
|
||
{
|
||
if (allDataRecords == null || allDataRecords.Count == 0)
|
||
{
|
||
MessageBox.Show("没有可导出的数据。请先开始采集。", "提示");
|
||
return;
|
||
}
|
||
|
||
SaveFileDialog sfd = new SaveFileDialog();
|
||
sfd.Filter = "CSV文件|*.csv";
|
||
sfd.FileName = $"采集数据_{DateTime.Now:yyyyMMdd_HHmmss}.csv";
|
||
if (sfd.ShowDialog() == DialogResult.OK)
|
||
{
|
||
try
|
||
{
|
||
ExportToCsv(sfd.FileName);
|
||
MessageBox.Show($"导出成功!共 {allDataRecords.Count} 条记录。", "完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"导出失败:{ex.Message}", "错误");
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ExportToCsv(string filePath)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"===== 开始导出,总记录数:{allDataRecords.Count} =====");
|
||
|
||
using (var writer = new System.IO.StreamWriter(filePath, false, Encoding.UTF8))
|
||
{
|
||
// 写入表头
|
||
var header = new List<string> { "序号", "时间" };
|
||
header.Add("低压(Pa)"); // AI0
|
||
header.Add("高压(kPa)"); // AI1
|
||
for (int i = 2; i < 16; i++) header.Add($"AI{i}(V)");
|
||
for (int i = 0; i < 32; i++) header.Add($"DI{i}");
|
||
for (int i = 0; i < 3; i++) header.Add($"PulseCount{i}");
|
||
for (int i = 0; i < 3; i++) header.Add($"Frequency{i}(Hz)");
|
||
writer.WriteLine(string.Join(",", header));
|
||
|
||
int rowCount = 0;
|
||
foreach (var record in allDataRecords)
|
||
{
|
||
var row = new List<string>
|
||
{
|
||
record.SampleIndex.ToString(),
|
||
record.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff")
|
||
};
|
||
|
||
for (int i = 0; i < 16; i++)
|
||
{
|
||
if (i == 0)
|
||
{
|
||
// 使用校准后的低压(Pa)
|
||
row.Add(record.LowPressureCalibrated.ToString("F1"));
|
||
if (rowCount < 10)
|
||
System.Diagnostics.Debug.WriteLine($"记录{rowCount}: 低压校准值={record.LowPressureCalibrated:F1}Pa");
|
||
}
|
||
else if (i == 1)
|
||
{
|
||
// 使用校准后的高压(Pa)
|
||
row.Add(record.HighPressureCalibrated.ToString("F1"));
|
||
}
|
||
else
|
||
{
|
||
row.Add(record.AI[i].ToString("F4"));
|
||
}
|
||
}
|
||
|
||
// DI 拆分
|
||
for (int b = 0; b < 4; b++)
|
||
{
|
||
byte diByte = record.DI[b];
|
||
for (int bit = 0; bit < 8; bit++)
|
||
{
|
||
row.Add(((diByte >> bit) & 1).ToString());
|
||
}
|
||
}
|
||
|
||
for (int i = 0; i < 3; i++) row.Add(record.PulseCount[i].ToString());
|
||
for (int i = 0; i < 3; i++) row.Add(record.Frequency[i].ToString());
|
||
|
||
writer.WriteLine(string.Join(",", row));
|
||
rowCount++;
|
||
}
|
||
|
||
System.Diagnostics.Debug.WriteLine($"===== 导出完成,实际写入行数:{rowCount} =====");
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
public class DataRecord
|
||
{
|
||
public DateTime Timestamp { get; set; } // 采样时间
|
||
public int SampleIndex { get; set; } // 采样序号(可选)
|
||
public float[] AI { get; set; } = new float[16]; // 16个模拟输入通道电压
|
||
public byte[] DI { get; set; } = new byte[4]; // 4个字节的DI状态(共32位)
|
||
public uint[] PulseCount { get; set; } = new uint[3]; // 3个脉冲计数器
|
||
public uint[] Frequency { get; set; } = new uint[3]; // 3个频率值
|
||
|
||
public float LowPressureCalibrated { get; set; } // 低压校准值(Pa)
|
||
public float HighPressureCalibrated { get; set; } // 高压校准值(Pa)
|
||
}
|
||
} |