如何控制Numato以太网继电器模块?C# WinForms命令无响应问题
Numato以太网继电器Telnet控制无响应问题排查与修复
问题描述
我开发了一个C# WinForms应用,用于控制8通道Numato以太网继电器模块。使用一个极简Telnet库,通过默认凭据(IP:169.254.1.1,端口:23,用户名:admin,密码:admin)能成功建立连接,但发送relay on 0这类控制命令后,设备无任何反应。以下是完整代码:
Telnet库代码
// minimalistic telnet implementation // conceived by Tom Janssens on 2007/06/06 for codeproject // // http://www.corebvba.be using System; using System.Collections.Generic; using System.Text; using System.Net.Sockets; namespace MinimalisticTelnet { enum Verbs { WILL = 251, WONT = 252, DO = 253, DONT = 254, IAC = 255 } enum Options { SGA = 3 } class TelnetConnection { TcpClient tcpSocket; int TimeOutMs = 100; public TelnetConnection(string Hostname, int Port) { tcpSocket = new TcpClient(Hostname, Port); } public string Login(string Username,string Password,int LoginTimeOutMs) { int oldTimeOutMs = TimeOutMs; TimeOutMs = LoginTimeOutMs; string s = Read(); if (!s.TrimEnd().EndsWith(":")) throw new Exception("Failed to connect : no login prompt"); WriteLine(Username); s += Read(); if (!s.TrimEnd().EndsWith(":")) throw new Exception("Failed to connect : no password prompt"); WriteLine(Password); s += Read(); TimeOutMs = oldTimeOutMs; return s; } public void WriteLine(string cmd) { Write(cmd + "\n"); } public void Write(string cmd) { if (!tcpSocket.Connected) return; byte[] buf = System.Text.ASCIIEncoding.ASCII.GetBytes(cmd.Replace("\0xFF","\0xFF\0xFF")); tcpSocket.GetStream().Write(buf, 0, buf.Length); } public string Read() { if (!tcpSocket.Connected) return null; StringBuilder sb=new StringBuilder(); do { ParseTelnet(sb); System.Threading.Thread.Sleep(TimeOutMs); } while (tcpSocket.Available > 0); return sb.ToString(); } public bool IsConnected { get { return tcpSocket.Connected; } } void ParseTelnet(StringBuilder sb) { while (tcpSocket.Available > 0) { int input = tcpSocket.GetStream().ReadByte(); switch (input) { case -1 : break; case (int)Verbs.IAC: // interpret as command int inputverb = tcpSocket.GetStream().ReadByte(); if (inputverb == -1) break; switch (inputverb) { case (int)Verbs.IAC: //literal IAC = 255 escaped, so append char 255 to string sb.Append(inputverb); break; case (int)Verbs.DO: case (int)Verbs.DONT: case (int)Verbs.WILL: case (int)Verbs.WONT: // reply to all commands with "WONT", unless it is SGA (suppres go ahead) int inputoption = tcpSocket.GetStream().ReadByte(); if (inputoption == -1) break; tcpSocket.GetStream().WriteByte((byte)Verbs.IAC); if (inputoption == (int)Options.SGA ) tcpSocket.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WILL:(byte)Verbs.DO); else tcpSocket.GetStream().WriteByte(inputverb == (int)Verbs.DO ? (byte)Verbs.WONT : (byte)Verbs.DONT); tcpSocket.GetStream().WriteByte((byte)inputoption); break; default: break; } break; default: sb.Append( (char)input ); break; } } } } }
窗体加载连接代码
using System; using System.Net.Sockets; using System.Windows.Forms; using MinimalisticTelnet; TelnetConnection tc; private void Form1_Load(object sender, EventArgs e) { tc = new TelnetConnection("169.254.1.1", 23); string s = tc.Login("admin", "admin", 100); textBox1.Text = s; }
按钮发送命令代码
private void button1_Click(object sender, EventArgs e) { if (tc.IsConnected) { tc.WriteLine("relay on 0"); textBox2.Text = tc.Read(); } }
排查与修复方案
1. 修正命令结束符
Numato设备要求命令以**回车(\r)**而非换行(\n)作为结束标记,当前代码使用的\n可能导致设备无法识别命令。
修改TelnetConnection类的WriteLine方法:
public void WriteLine(string cmd) { Write(cmd + "\r"); }
2. 增加命令处理延迟
设备处理命令需要短暂时间,发送后立即读取可能无法捕获响应,需增加延迟:
private void button1_Click(object sender, EventArgs e) { if (tc.IsConnected) { tc.WriteLine("relay on 0"); System.Threading.Thread.Sleep(200); // 等待设备处理命令 textBox2.Text = tc.Read(); } }
3. 跳过Telnet选项协商(备选方案)
部分Numato设备对Telnet选项协商兼容性较差,可直接使用TcpClient发送命令:
TcpClient client; NetworkStream stream; private void Form1_Load(object sender, EventArgs e) { client = new TcpClient("169.254.1.1", 23); stream = client.GetStream(); // 读取登录提示符 byte[] buffer = new byte[1024]; int bytesRead = stream.Read(buffer, 0, buffer.Length); // 发送用户名 byte[] username = Encoding.ASCII.GetBytes("admin\r"); stream.Write(username, 0, username.Length); stream.Read(buffer, 0, buffer.Length); // 发送密码 byte[] password = Encoding.ASCII.GetBytes("admin\r"); stream.Write(password, 0, password.Length); bytesRead = stream.Read(buffer, 0, buffer.Length); textBox1.Text = Encoding.ASCII.GetString(buffer, 0, bytesRead); } private void button1_Click(object sender, EventArgs e) { if (client.Connected) { byte[] command = Encoding.ASCII.GetBytes("relay on 0\r"); stream.Write(command, 0, command.Length); System.Threading.Thread.Sleep(200); byte[] buffer = new byte[1024]; int bytesRead = stream.Read(buffer, 0, buffer.Length); textBox2.Text = Encoding.ASCII.GetString(buffer, 0, bytesRead); } }
4. 验证通道编号
确认设备通道编号规则:部分Numato型号通道从1开始而非0,尝试发送relay on 1测试。
内容的提问来源于stack exchange,提问作者Mark Ryan Morales
相关产品推荐
相关产品推荐

