Files
pressurediff/压差法气体渗透仪/NET_HRF4838_C#/Form1.cs
2026-03-09 09:45:59 +08:00

1048 lines
42 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 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
}
}