基于C#的工业机器人上位机控制程序开发(搭配松下PLC + MC协议)
·
以下是为《基于C#的工业机器人上位机控制程序开发(搭配松下PLC + MC协议)》这篇文章补充的更多实用代码示例,覆盖工业现场最常遇到的功能模块。这些代码基于 .NET 8/9 + 自封装 MC 协议客户端,2026年主流工业做法。
1. 完整的心跳 + 自动重连机制(工业必备)
public class PanasonicMCClientWithReconnect : IDisposable
{
private readonly string ip;
private readonly int port = 5000;
private TcpClient client;
private NetworkStream stream;
private CancellationTokenSource cts = new();
private Task heartbeatTask;
public event Action<string> OnConnectionStatusChanged;
public PanasonicMCClientWithReconnect(string ipAddress)
{
ip = ipAddress;
StartHeartbeat();
}
private void StartHeartbeat()
{
heartbeatTask = Task.Run(async () =>
{
while (!cts.IsCancellationRequested)
{
try
{
if (client == null || !client.Connected)
{
await ReconnectAsync();
}
else
{
// 心跳:读一个固定寄存器 D0(假设始终为固定值)
var data = await ReadWordDevicesAsync("D0", 1);
if (data == null || data.Length == 0)
throw new Exception("心跳失败");
}
}
catch
{
OnConnectionStatusChanged?.Invoke("断开,正在重连...");
await Task.Delay(2000);
}
await Task.Delay(5000); // 每5秒心跳一次
}
});
}
private async Task ReconnectAsync()
{
try
{
client?.Dispose();
client = new TcpClient();
await client.ConnectAsync(ip, port);
stream = client.GetStream();
OnConnectionStatusChanged?.Invoke("已连接");
}
catch
{
OnConnectionStatusChanged?.Invoke("连接失败");
}
}
public async Task<ushort[]> ReadWordDevicesAsync(string device, int count)
{
if (client == null || !client.Connected) return null;
// 原有读逻辑...
// 这里省略,参考上一篇完整封装
return new ushort[count]; // 简化示例
}
public void Dispose()
{
cts.Cancel();
heartbeatTask?.Wait(1000);
stream?.Dispose();
client?.Dispose();
}
}
2. 批量读取机器人多状态(推荐方式,减少通信次数)
// 假设PLC中机器人状态集中存储在 D1000 ~ D1019
private async Task UpdateRobotMultiStatus()
{
try
{
var data = await mc.ReadWordDevicesAsync("D1000", 20);
// 示例映射(根据你的PLC程序调整地址)
int robotRunning = data[0]; // 0=停止 1=运行
int currentProgNo = data[1]; // 当前程序号
int alarmCode = data[2]; // 主报警码
int servoOn = data[3]; // 伺服使能状态
int estopActive = data[4]; // 急停信号
float currentSpeed = data[5] / 10f; // 速度百分比(放大10倍存储)
int posX = data[6]; // 当前X位置(脉冲或mm)
int posY = data[7];
int posZ = data[8];
int posR = data[9];
this.Invoke((MethodInvoker)delegate
{
lblRunning.Text = robotRunning == 1 ? "运行中" : "停止";
lblProgNo.Text = $"程序:{currentProgNo:D3}";
lblAlarmCode.Text = alarmCode == 0 ? "无报警" : $"报警码:{alarmCode:X4}";
lblServo.Text = servoOn == 1 ? "伺服ON" : "伺服OFF";
lblEstop.Text = estopActive == 1 ? "急停触发" : "正常";
lblSpeed.Text = $"{currentSpeed:F1}%";
lblPosition.Text = $"X:{posX} Y:{posY} Z:{posZ} R:{posR}";
});
}
catch (Exception ex)
{
lblStatus.Text = "读取状态失败:" + ex.Message;
}
}
3. 手动点动(Jog)控制(带速度调节 + 安全检查)
private float jogSpeedPercent = 10.0f; // 默认10%
private async void BtnJogAxis_MouseDown(object sender, MouseEventArgs e)
{
if (!await CheckSafetyInterlock()) return;
Button btn = sender as Button;
string axis = btn.Tag?.ToString(); // "X+", "X-", "Y+", "Y-" 等
// 写点动方向和速度
await mc.WriteWordDeviceAsync("D300", (ushort)(jogSpeedPercent * 10)); // D300 = 速度(放大10倍)
ushort directionCode = axis switch
{
"X+" => 1,
"X-" => 2,
"Y+" => 3,
"Y-" => 4,
"Z+" => 5,
"Z-" => 6,
_ => 0
};
await mc.WriteWordDeviceAsync("D301", directionCode); // D301 = 方向码
// 启动点动(M200 = 1 上升沿)
await mc.WriteBitDeviceAsync("M200", true);
}
private async void BtnJogAxis_MouseUp(object sender, MouseEventArgs e)
{
// 停止点动
await mc.WriteBitDeviceAsync("M200", false);
await mc.WriteWordDeviceAsync("D301", 0); // 清方向
}
4. 报警历史记录与复位(带时间戳 + SQLite)
private async void LogAlarm(int alarmCode)
{
if (alarmCode == 0) return;
string desc = GetAlarmDescription(alarmCode); // 自建报警码表
using var conn = new SQLiteConnection("Data Source=robot_alarms.db");
await conn.OpenAsync();
using var cmd = conn.CreateCommand();
cmd.CommandText = @"
INSERT INTO AlarmLog (Timestamp, Code, Description)
VALUES (@ts, @code, @desc)";
cmd.Parameters.AddWithValue("@ts", DateTime.Now);
cmd.Parameters.AddWithValue("@code", alarmCode);
cmd.Parameters.AddWithValue("@desc", desc ?? "未知报警");
await cmd.ExecuteNonQueryAsync();
// UI显示最新报警
this.Invoke(() => lblLatestAlarm.Text = $"最新报警:{alarmCode:X4} - {desc}");
}
// 报警复位按钮
private async void BtnResetAlarm_Click(object sender, EventArgs e)
{
if (!await CheckSafetyInterlock()) return;
// 写复位标志 M300 = 1 → 上升沿触发
await mc.WriteBitDeviceAsync("M300", true);
await Task.Delay(200);
await mc.WriteBitDeviceAsync("M300", false);
MessageBox.Show("报警已复位");
}
5. 产量/节拍统计(每周期记录一次)
private int cycleCount = 0;
private DateTime lastCycleTime = DateTime.Now;
private async void TimerCycle_Tick(object sender, EventArgs e)
{
var data = await mc.ReadWordDevicesAsync("D500", 1); // 假设D500为周期完成计数器
int currentCycle = data[0];
if (currentCycle > cycleCount)
{
cycleCount = currentCycle;
var now = DateTime.Now;
double cycleTimeSec = (now - lastCycleTime).TotalSeconds;
lastCycleTime = now;
this.Invoke(() =>
{
lblCycleCount.Text = $"已完成周期:{cycleCount}";
lblCycleTime.Text = $"本周期耗时:{cycleTimeSec:F2} 秒";
lblUPH.Text = cycleTimeSec > 0 ? $"UPH:{3600 / cycleTimeSec:F1}" : "--";
});
// 可存入数据库做日报表
}
}
这些代码片段覆盖了工业机器人上位机最核心的几个模块:心跳重连、批量状态刷新、手动点动、安全联锁、报警管理、节拍统计。
如果你希望进一步补充某个具体功能(例如:
- 完整的报警码表 + 描述映射
- 多机器人同时监控(多实例 MCClient)
- 机器人位置实时曲线图(ScottPlot)
- 触摸屏适配 + 高DPI处理
- 离线模式 + 数据缓存
请告诉我,我可以继续给出更针对性的完整示例代码。
更多推荐
所有评论(0)