using System;
using System.ComponentModel;
using System.Net;
using System.Text;
using System.Windows.Forms;
using HslCommunication;
using HslCommunication.ModBus;
using Newtonsoft.Json;
using StackExchange.Redis;
using static System.Net.WebRequestMethods;
namespace WinFormsApp2
{
public partial class Form1 : Form
{
private ModbusTcpNet plcClient;
private TextBox txtResult;
private Button btnConnect;
private Button btnRead;
private Button btnDisconnect;
private ComboBox cboRegisterType;
private TextBox txtStartAddress;
private TextBox txtLength;
private Label lblRegisterType;
private Label lblStartAddress;
private Label lblLength;
private CheckBox chkHexFormat; // 添加十六进制格式选项
private BackgroundWorker plcWorker;
private HttpListener httpListener;
private Thread serverThread;
private bool isServerRunning = false;
//http://localhost:5000/api/devices 获取所有的IP
//http://localhost:5000/api/device?id=device:ip-192.168.1.88 //获取单独IP的数据
private const string ApiBaseUrl = "http://localhost:5000/";
private ConnectionMultiplexer _redisConnection;
private IDatabase _redisDb;
public Form1()
{
InitializeHttpServer();
// 添加控件到窗体
InitializeRedis();
// 初始化后台工作组件
plcWorker = new BackgroundWorker();
plcWorker.WorkerSupportsCancellation = true;
plcWorker.DoWork += PlcWorker_DoWork;
plcWorker.RunWorkerCompleted += PlcWorker_RunWorkerCompleted;
InitializeComponent();
// 事件绑定
//btnRead.Click += (s, e) => ReadPLCData();
//btnDisconnect.Click += (s, e) => DisconnectPLC();
////btnConnect.Click += (s, e) => ConnectToPLC();
// 修改事件绑定
btnRead.Click += (s, e) => StartPlcReading();
btnDisconnect.Click += (s, e) => StopPlcReading();
}
// 启动PLC读取
private void StartPlcReading()
{
if (plcWorker.IsBusy)
{
AppendResultText("PLC读取已在后台运行");
return;
}
btnRead.Enabled = false;
btnDisconnect.Enabled = true;
AppendResultText("启动PLC后台读取...");
// 获取要连接的PLC列表
string[] plcList = txtStartAddress.Text.Split(',');
// 启动后台工作
plcWorker.RunWorkerAsync(plcList);
}
private void InitializeRedis()
{
var config = new ConfigurationOptions
{
EndPoints = { "localhost:6379" },
Password = "ymkj",
AbortOnConnectFail = false,
ConnectRetry = 3,
ConnectTimeout = 5000
};
_redisConnection = ConnectionMultiplexer.Connect(config);
_redisDb = _redisConnection.GetDatabase();
}
// 停止PLC读取
private void StopPlcReading()
{
if (plcWorker.IsBusy)
{
plcWorker.CancelAsync();
AppendResultText("正在停止PLC后台读取...");
}
else
{
AppendResultText("没有正在运行的PLC读取任务");
}
btnRead.Enabled = true;
btnDisconnect.Enabled = false;
}
// 后台工作执行
private void PlcWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (true) {
BackgroundWorker worker = (BackgroundWorker)sender;
string[] plcList = (string[])e.Argument;
foreach (string plcInfo in plcList)
{
// 检查是否请求取消
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
string[] plcDetails = plcInfo.Split(':');
if (plcDetails.Length < 2)
{
AppendResultText($"无效的PLC配置: {plcInfo}");
continue;
}
string ip = plcDetails[0];
string port = plcDetails[1];
try
{
AppendResultText($"连接PLC: {ip}:{port}");
ConnectToPLC(ip, port);
if (plcClient == null)
{
AppendResultText($"无法连接到PLC: {ip}:{port}");
continue;
}
AppendResultText($"开始读取PLC数据: {ip}:{port}");
// 读取数据
ReadMBits(0, 2005);
ReadXBits(0, 35);
ReadYBits(0, 31);
ReadDRegisters(1, 3428);
AppendResultText($"完成读取PLC数据: {ip}:{port}");
}
catch (Exception ex)
{
AppendResultText($"读取PLC数据异常({ip}:{port}): {ex.Message}");
}
finally
{
// 添加延迟,避免过于频繁的连接
Thread.Sleep(1000);
}
// 检查是否请求取消
if (worker.CancellationPending)
{
e.Cancel = true;
return;
}
}
}
}
// 后台工作完成
private void PlcWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
AppendResultText("PLC后台读取已取消");
}
else if (e.Error != null)
{
AppendResultText($"PLC后台读取发生错误: {e.Error.Message}");
}
else
{
AppendResultText("PLC后台读取完成");
}
btnRead.Enabled = true;
btnDisconnect.Enabled = false;
}
private void InitializeHttpServer()
{
httpListener = new HttpListener();
httpListener.Prefixes.Add(ApiBaseUrl);
// 添加API路由
this.Controls.Add(new Label
{
Text = $"API服务地址: {ApiBaseUrl}",
Location = new System.Drawing.Point(0, 0),
AutoSize = true
});
// 添加服务器控制按钮
var btnStartServer = new Button
{
Text = "启动API服务",
Location = new System.Drawing.Point(430, 0),
AutoSize = true
};
btnStartServer.Click += (s, e) => StartHttpServer();
var btnStopServer = new Button
{
Text = "停止API服务",
Location = new System.Drawing.Point(590, 0),
AutoSize = true
};
btnStopServer.Click += (s, e) => StopHttpServer();
this.Controls.Add(btnStartServer);
this.Controls.Add(btnStopServer);
}
// 启动HTTP服务器
private void StartHttpServer()
{
if (isServerRunning) return;
try
{
httpListener.Start();
isServerRunning = true;
serverThread = new Thread(ListenForRequests);
serverThread.IsBackground = true;
serverThread.Start();
AppendResultText($"HTTP服务器已启动,监听地址: {ApiBaseUrl}");
}
catch (Exception ex)
{
AppendResultText($"启动HTTP服务器失败: {ex.Message}");
}
}
// 停止HTTP服务器
private void StopHttpServer()
{
if (!isServerRunning) return;
isServerRunning = false;
httpListener.Stop();
AppendResultText("HTTP服务器已停止");
}
// 监听HTTP请求
private void ListenForRequests()
{
while (isServerRunning)
{
try
{
var context = httpListener.GetContext();
ThreadPool.QueueUserWorkItem(ProcessRequest, context);
}
catch (Exception ex)
{
AppendResultText($"HTTP监听异常: {ex.Message}");
}
}
}
// 处理HTTP请求
private void ProcessRequest(object state)
{
var context = state as HttpListenerContext;
var request = context.Request;
var response = context.Response;
// 统一设置CORS头,允许来自 http://localhost:8080 的请求
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Methods", "*");
response.Headers.Add("Access-Control-Allow-Headers", "*");
response.Headers.Add("Access-Control-Max-Age", "86400");
// 处理OPTIONS请求
if (request.HttpMethod == "OPTIONS")
{
response.StatusCode = 200;
response.Close();
return;
}
try
{
var path = request.Url.AbsolutePath.ToLower();
switch (path)
{
case "/api/devices":
HandleGetDevices(context);
break;
case "/api/device":
HandleGetDevice(context);
break;
default:
SendResponse(response, "Not Found", 404);
break;
}
}
catch (Exception ex)
{
SendResponse(response, $"Error: {ex.Message}", 500);
}
finally
{
}
}
// 处理获取所有设备请求
private void HandleGetDevices(HttpListenerContext context)
{
var response = context.Response;
// 使用全局Redis连接
if (_redisDb == null || _redisConnection == null)
{
SendResponse(response, "Redis连接未初始化", 500);
return;
}
try
{
// 获取所有设备键
var server = _redisConnection.GetServer("localhost", 6379);
var deviceKeys = server.Keys(pattern: "device:*").ToArray();
// 构建设备列表
var devices = new List<object>();
foreach (var key in deviceKeys)
{
// 直接使用key.ToString()避免多次转换
string keyStr = key.ToString();
// 更安全的IP提取方式
string ip = keyStr.StartsWith("device:ip-")
? keyStr.Substring("device:ip-".Length)
: keyStr;
devices.Add(new { id = keyStr, ip });
}
var result = new
{
success = true,
count = devices.Count,
devices
};
SendJsonResponse(response, result);
}
catch (Exception ex)
{
SendResponse(response, $"获取设备列表失败: {ex.Message}", 500);
}
}
// 处理获取特定设备数据请求
private void HandleGetDevice(HttpListenerContext context)
{
var response = context.Response;
var request = context.Request;
var deviceId = request.QueryString["id"];
// 验证设备ID参数
if (string.IsNullOrEmpty(deviceId))
{
SendResponse(response, "缺少设备ID参数", 400);
return;
}
// 使用全局Redis连接
if (_redisDb == null || _redisConnection == null)
{
SendResponse(response, "Redis连接未初始化", 500);
return;
}
try
{
// 检查设备是否存在
if (!_redisDb.KeyExists(deviceId))
{
SendResponse(response, $"设备不存在: {deviceId}", 404);
return;
}
// 获取设备所有数据
var entries = _redisDb.HashGetAll(deviceId);
// 转换为字典
var data = new Dictionary<string, string>();
foreach (var entry in entries)
{
data[entry.Name] = entry.Value;
}
var result = new
{
success = true,
device = deviceId,
data
};
SendJsonResponse(response, result);
}
catch (Exception ex)
{
// 更详细的错误日志
AppendResultText($"获取设备数据失败({deviceId}): {ex}");
SendResponse(response, $"获取设备数据失败: {ex.Message}", 500);
}
}
// 发送JSON响应
private void SendJsonResponse(HttpListenerResponse response, object data)
{
try
{
response.ContentType = "application/json";
response.ContentEncoding = Encoding.UTF8;
var json = JsonConvert.SerializeObject(data);
var buffer = Encoding.UTF8.GetBytes(json);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
}
finally
{
if (response.OutputStream.CanWrite)
{
response.OutputStream.Close();
}
}
}
// 发送文本响应
private void SendResponse(HttpListenerResponse response, string message, int statusCode)
{
try
{
// 记录错误信息
AppendResultText($"[HTTP {statusCode}] {message}");
response.StatusCode = statusCode;
response.ContentType = "text/plain";
response.ContentEncoding = Encoding.UTF8;
var buffer = Encoding.UTF8.GetBytes(message);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
}
finally
{
response.OutputStream.Close();
}
}
private void ReadMBits(int start, int length)
{
// 使用数字地址读取M寄存器(辅助继电器)
// M寄存器通常映射到Modbus的线圈地址(0x)
// 起始地址 = start + 0(根据PLC型号可能需要偏移)
OperateResult<bool[]> readResult = plcClient.ReadCoil(start.ToString(), (ushort)length);
if (readResult.IsSuccess)
{
StringBuilder sb = new StringBuilder();
var hashEntries = new List<HashEntry>();
sb.AppendLine($"M寄存器读取成功 (M{start} - M{start + length - 1}):");
for (int i = 0; i < readResult.Content.Length; i++)
{
sb.AppendLine($"M{start + i}: {readResult.Content[i]}");
string field = $"M{start + i}";
string value = readResult.Content[i].ToString();
hashEntries.Add(new HashEntry(field, value));
}
AppendResultText(sb.ToString());
SetRedis(hashEntries);
}
else
{
AppendResultText($"M寄存器读取失败: {readResult.Message}");
}
}
private void ReadXBits(int start, int length)
{
// 使用数字地址读取X寄存器(输入继电器)
// X寄存器通常映射到Modbus的离散输入地址(1x)
// 起始地址 = start + 0(根据PLC型号可能需要偏移)
OperateResult<bool[]> readResult = plcClient.ReadDiscrete(start.ToString(), (ushort)length);
if (readResult.IsSuccess)
{
StringBuilder sb = new StringBuilder();
var hashEntries = new List<HashEntry>();
sb.AppendLine($"X寄存器读取成功 (X{start} - X{start + length - 1}):");
for (int i = 0; i < readResult.Content.Length; i++)
{
sb.AppendLine($"X{start + i}: {readResult.Content[i]}");
string field = $"X{start + i}";
string value = readResult.Content[i].ToString();
hashEntries.Add(new HashEntry(field, value));
}
AppendResultText(sb.ToString());
SetRedis(hashEntries);
}
else
{
AppendResultText($"X寄存器读取失败: {readResult.Message}");
}
}
private void ReadYBits(int start, int length)
{
// 使用数字地址读取Y寄存器(输出继电器)
// Y寄存器通常映射到Modbus的线圈地址(0x)
// 起始地址 = start + 0(根据PLC型号可能需要偏移)
OperateResult<bool[]> readResult = plcClient.ReadCoil(start.ToString(), (ushort)length);
if (readResult.IsSuccess)
{
StringBuilder sb = new StringBuilder();
var hashEntries = new List<HashEntry>();
sb.AppendLine($"Y寄存器读取成功 (Y{start} - Y{start + length - 1}):");
for (int i = 0; i < readResult.Content.Length; i++)
{
sb.AppendLine($"Y{start + i}: {readResult.Content[i]}");
string field = $"Y{start + i}";
string value = readResult.Content[i].ToString();
hashEntries.Add(new HashEntry(field, value));
}
AppendResultText(sb.ToString());
SetRedis(hashEntries);
}
else
{
AppendResultText($"Y寄存器读取失败: {readResult.Message}");
}
}
private void ReadDRegisters(int start, int length)
{
// 使用数字地址读取D寄存器(数据寄存器)
// D寄存器通常映射到Modbus的保持寄存器地址(4x)
// 起始地址 = start + 0(根据PLC型号可能需要偏移)
OperateResult<short[]> readResult = plcClient.ReadInt16(start.ToString(), (ushort)length);
if (readResult.IsSuccess)
{
StringBuilder sb = new StringBuilder();
var hashEntries = new List<HashEntry>();
sb.AppendLine($"D寄存器读取成功 (D{start} - D{start + length - 1}):");
for (int i = 0; i < readResult.Content.Length; i++)
{
//if (chkHexFormat.Checked)
//{
// sb.AppendLine($"D{start + i}: 0x{readResult.Content[i]:X4} (十进制: {readResult.Content[i]})");
// string field = $"D{start + i}";
// string value = readResult.Content[i].ToString();
// hashEntries.Add(new HashEntry(field, value));
//}
//else
//{
sb.AppendLine($"D{start + i}: {readResult.Content[i]}");
string field = $"D{start + i}";
string value = readResult.Content[i].ToString();
hashEntries.Add(new HashEntry(field, value));
//}
}
AppendResultText(sb.ToString());
SetRedis(hashEntries);
}
else
{
AppendResultText($"D寄存器读取失败: {readResult.Message}");
}
}
private void ConnectToPLC(string ip, string duration)
{
try
{
// 创建Modbus TCP客户端(替换为实际PLC的IP和端口)
plcClient = new ModbusTcpNet(ip, int.Parse(duration));
// 尝试连接
OperateResult connectResult = plcClient.ConnectServer();
if (connectResult.IsSuccess)
{
AppendResultText("PLC连接成功!");
btnRead.Enabled = true;
btnDisconnect.Enabled = true;
}
else
{
AppendResultText($"连接失败: {connectResult.Message}");
plcClient = null;
}
}
catch (Exception ex)
{
AppendResultText($"连接异常: {ex.Message}");
}
}
private void AppendResultText(string text)
{
if (txtResult.InvokeRequired)
{
txtResult.Invoke(new Action<string>(AppendResultText), text);
}
else
{
txtResult.AppendText($"[{DateTime.Now:HH:mm:ss}] {text}\r\n");
txtResult.ScrollToCaret();
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
plcClient?.ConnectClose();
}
//private void SetRedis(List<HashEntry> hashEntries)
//{
// var config = new ConfigurationOptions
// {
// EndPoints = { "localhost:6379" },
// Password = "ymkj", // 确保密码正确
// AbortOnConnectFail = false,
// ConnectTimeout = 10000, // 可选:增加连接超时时间
// SyncTimeout = 10000 // 可选:增加同步操作超时时间
// };
// using (var redis = ConnectionMultiplexer.Connect(config))
// {
// var db = redis.GetDatabase();
// // 读取数据
// // 获取全部字段(返回HashEntry数组)
// db.HashSet("device:"+"ip-"+plcClient.IpAddress, hashEntries.ToArray());
// }
//}
private void SetRedis(List<HashEntry> hashEntries)
{
try
{
string deviceKey = $"device:ip-{plcClient.IpAddress}";
_redisDb.HashSet(deviceKey, hashEntries.ToArray());
}
catch (Exception ex)
{
AppendResultText($"Redis存储失败: {ex.Message}");
}
}
}
} Form1.cs 设计没显示出来