Spiga

通信协议:SECS/GEM

2026-03-28 11:53:07

一、SECS/GEM

1. SECS/GEM通信

  • Semiconductor Equipment Communication Standard/Generic Equipment Model(半导体通用设备通信标准/通用设备模型)是半导体制造设备之间进行通信的标准协议。它定义了一套标准消息格式和通信规则,以便设备能够有效地与制造执行系统(MES)或工厂自动化系统进行通信和数据交换。
  • 主要用于半导体制造设备,如晶圆加工机、封装机、测试设备等。这些设备需要通过SECS/GEM协议与工厂自动化系统进行数据交换和控制。
  • 在高度自动化的半导体制造厂中,CIM(Computer Integrated Manufacturing【计算机整合制造】) 统一管理各设备的生产流程,并随时监控设备过程的状态,以减少过程失误进而降低成本及提升产品的质量。但随着过程的不同,各设备有着不同特性的差异且各制造商所提供的设备也不尽相同,因此增加CIM自动化管理的困难与复杂程度。软件集成自动化存在的主要问题是在不同的设备供应商之间没有标准的通讯协议。设备供应商不向半导体生产商开放通讯协议及接口软件,这使得半导体生产商不得不建立他们自己的软件“连接”,导致了项目费用的巨大增加。
  • SECS/GEM是由国际半导体设备与材料协会(SEMI)的会员一起构建的连接性标准。应用于在设备和工厂管理系统间的通讯标准。是半导体的设备接口协议,用于设备到主机的数据通讯。经过引入SECS/GEM,制造可以搜集更多的信息,SECS/GEM使设备和主机中心可以畅通无阻地停止通讯,从而完成智能工厂自动化。

2. 仿真环境

  • SEComSimulator — 需要.NET Framework 3.5
  • SECS Emulator 功能上是一样 建议使用上面的
  • WireShark / TCP调试助手(报文测试)

3. SECS/GEM结构与流程

  • 协议结构

  • 通信方式:RS232(SerialPort)、TCP/IP(Socket—IP地址 Port 进行Connect)

  • 通信流程-HSMS

4. 消息报文主体结构

Message Length (4 Bytes) Message Header (10 Bytes) Message Text (0 to ~ 8 Mbytes)
  • Header结构

  • 状态报文

  • GEM指令说明

指令很多,一般是根据实际的文档来部分开发

主要分类说明

5. SECS/GEM消息

前6位表示类型,后面2位表示长度,如

  • 41(01000001):1 Bytes ASCII data(up to 256 bytes)
  • 42(01000010):2 Bytes ASCII data(up to 64k bytes)
  • 43(01000011):3 Bytes ASCII data(up to 7.99 Mbytes)

6. 常用指令

  • 通常在初始化时首次连接到设备时使用
SECS-II Description
S1F1 向设备说声你好
S1F13 与设备建立通信
S2F15 设置设备常数
S2F43 关闭后台打印
S2F33 创建 / 删除报告
S2F35 将报表关联到事件
S2F37 启用/禁用事件
S5F3 启用/禁用警报
  • 通常用于配方选择、远程启动、晶圆选择等
SECS-II Description
S2F41 远程设备控制
  • 通常用于数据收集,Euipment 发送给 Host,是主动发送的
SECS-II Description
S6F3 旧版本的事件报告
S6F9 旧版本的事件报告
S6F11 Event Reports

S6F3是机台主动上报给host的一种消息类型。它是SECS协议中的一种事件报告消息,用于向上层主机报告机台的状态和事件信息。机台可以在特定的条件下主动发送S6F3消息,例如当发生故障、完成特定的任务或达到特定的状态时。主机可以通过接收S6F3消息来了解机台的状态和事件信息,以便进行相应的处理和控制。

  • S5F1用于处理设备报警
  • S9 这一类用于当设备检测到之前的消息错误时,由设备发送。

7. 时间

  • T3:从发送端发送Primary Message到接收到响应端回复的Secondary Message之间的时间间隔
  • T4:多块传输中,接收到相邻块之间的时间间隔

  • T5(默认10秒):连接间隔时间,表示两个连接请求之间的时间间隔,指断开连接和重新连接的最小时间(不能频繁请求连接,一个连接失败后,必须等待T5时间后再发送连接请求)。
  • T6(默认5秒):控制会话超时,表示一个控制回话所能开启的最长时间,发送req消息时T6开启,如果未在T6超时之前收到rsp消息,则断开连接。
  • T7(默认10秒):Select状态超时,建立了socket连接后,必须于T7时间内完成Select操作,否则断开连接。
  • T8(默认5秒):网络字符超时,表示成功接收到单个HSMS消息的字符之间的最大时间间隔。

9. 关键字说明

  • Host 主机一般指向工厂控制系统
  • EQP 单机设备
  • Active 在Tcp通信中主动连接对方的(可理解为客户端)
  • Passive 被动等待对方连接(可理解为服务器)
  • Device ID 设备标识
  • CEID 事件编号
  • SVID 设备状态编号
  • VID 设备变量
  • IDRPTID 报告编号
  • PPID 配方编号
  • ALCD 报警清除或者报警设置
  • ALID 报警编号
  • ALTX 报警文本

二、通信库的封装

1. 定义枚举与数据字典

public enum DataType
{
    LIST = 0b00000000,
    BINARY = 0b00100000,
    BOOLEAN = 0b00100100,
    ASCII = 0b01000000,
    JIS_8 = 0b01000100,
    CHARACTER_2 = 0b01001000,
    INT_8 = 0b01100000,
    INT_2 = 0b011000100,
    INT_4 = 0b01110000,
    FLOAT = 0b10000000,
    DOUBLE = 0b10010000,
    UINT_8 = 0b10100000,
    UINT_1 = 0b10100100,
    UINT_2 = 0b10101000,
    UINT_4 = 0b10110000,
}

public enum Identity
{
    /// <summary>
    /// 主机
    /// </summary>
    HOST, 
    
    /// <summary>
    /// 设备
    /// </summary>
    EQUIPMENT
}

public enum Mode
{
    /// <summary>
    /// 主动
    /// </summary>
    ACTIVE, 
    
    /// <summary>
    /// 被动
    /// </summary>
    PASSIVE
}

internal class Status
{
    public static Dictionary<byte, string> Errors = new Dictionary<byte, string>()
    {
        {0x00,"Abort Transaction" },
        {0x01,"Unrecognized Device ID" },
        {0x03,"Unrecognized Stream Type" },
        {0x05,"Unrecognized Function Type" },
        {0x07,"Illegal Data" },
        {0x09,"Transaction Timer Timeout" },
    };

    public static readonly int T3 = 45_000;
    public static readonly int T4 = 10_000;
    public static readonly int T5 = 10_000;
    public static readonly int T6 = 5_000;
    public static readonly int T7 = 10_000;
    public static readonly int T8 = 5_000;
    public static readonly int BeatInterval =15_000;
}

2. 封装连接方法

private byte[] _deviceId;

Random rand = new Random();
Socket socket;
public HSMS()
{
    socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
}

public void Connect(string host, int port)
{
    socket.Connect(host, port); // 3次握手

    // 发送Select状态请求
    byte[] system_bytes = new byte[4] {
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255)
    };
    byte[] select_bytes = new byte[] {
        0x00,0x00,0x00,0x0A,// 数据按大端处理
        0xFF,0xFF,
        0x00,0x00,0x00,
        0x01,
        system_bytes[0],
        system_bytes[1],
        system_bytes[2],
        system_bytes[3],
    };
    byte[] resp = SendAndReceive(select_bytes);
    // S1F2  -  DeviceId
    if (resp[2] != 0 || resp[3] != 0)
    {
        if (resp[2] == 9)
            if (Status.Errors.ContainsKey(resp[3]))
                throw new Exception(Status.Errors[resp[3]]);
            else
                throw new Exception("响应异常");
    }
    if (resp[5] != 2)
        throw new Exception("响应异常");

    _deviceId = resp.Take(2).ToArray();
}

private byte[] SendAndReceive(byte[] bytes)
{
    socket.Send(bytes);

    byte[] resp = new byte[4];
    socket.Receive(resp);
    Array.Reverse(resp);
    int len = BitConverter.ToInt32(resp, 0);

    resp = new byte[len];
    socket.Receive(resp);

    return resp;
}

3. 封装消息对象

public class Message
{
    public DataType DataType { get; set; }

    public byte[] Data { get; set; }

    public int Length { get; set; }

    public List<Message> SubNode { get; set; } = new List<Message>();
}

internal class MessageMode
{
    public byte[] SystemBytes { get; set; }

    public byte[] MessageBytes { get; set; }

    public DateTime RecTime { get; set; } = DateTime.Now;
}

4. 实现S1F1指令

public void S1F1()
{
    byte[] system_bytes = new byte[4] {
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255)
    };
    byte[] S1F1 = new byte[] {
        0x00,0x00,0x00,0x0A,
        _deviceId[0],_deviceId[1],   // DeviceID
        0x81,0x01,
        0x00,
        0x00,
        system_bytes[0],
        system_bytes[1],
        system_bytes[2],
        system_bytes[3]
    };
    byte[] resp = SendAndReceive(S1F1);

    if (resp[2] != 1 || resp[3] != 2)
    {
        if (resp[2] == 9)
            if (Status.Errors.ContainsKey(resp[3]))
                throw new Exception(Status.Errors[resp[3]]);
        else
            throw new Exception("响应异常");
    }
    byte[] sb = resp.Skip(6).Take(4).ToArray();
    if (!sb.SequenceEqual(system_bytes))
        throw new Exception("响应异常");
    
    // 获取Message Text
    byte[] txt_bytes = resp.Skip(10).ToArray();
    // 
    byte b = txt_bytes[0];
    byte type = (byte)(b & 0b11111100);
    DataType dt = (DataType)type;
    byte temp_len = (byte)(b & 0b00000011);/// 后面有多个字节表示数据长度
    int index = 1;

    Message msg = new Message();
    // 这里可能最多3个字节
    byte[] len_bytes = txt_bytes.Skip(index).Take(temp_len).ToArray();
    Array.Reverse(len_bytes);   // 如:01 02 03  -> 03 02 01
    int len = 0;  // 4byte
    // 这里需要考虑三种不同长度字节的转换情况
    for (int i = 0; i < temp_len; i++)
    {
        // 03  02 512    515   01 *256*256+515
        len += (int)(len_bytes[i] * Math.Pow(256, i));// 当前节点有多少子项
    }
    index += temp_len;

    if (dt == DataType.LIST)
    {
        msg.DataType = dt;
        msg.Length = len;
        if (len > 0)
        {
            resp.Skip(index).Take(len);
        }
    }
}

private Message GetMessage(byte[] msg_bytes)
{
    Message msg = new Message();
    // 第一个字节,表示类型和长度字节数
    byte b = msg_bytes[0];
    // 取出类型
    byte type = (byte)(b & 0b11111100);
    DataType dt = (DataType)type;
    // 取出长度字节数
    byte temp_len = (byte)(b & 0b00000011);/// 后面有多个字节表示数据长度
    // 下一个字节起始
    int index = 1;

    // 这里表示数据字节数   可能最多3个字节
    byte[] len_bytes = msg_bytes.Skip(index).Take(temp_len).ToArray();
    Array.Reverse(len_bytes);   // 如:01 02 03  -> 03 02 01
    int len = 0;  // 4byte
                  // 这里需要考虑三种不同长度字节的转换情况
    for (int i = 0; i < temp_len; i++)
    {
        // 03  02 512    515   01 *256*256+515
        // 字节数
        len += (int)(len_bytes[i] * Math.Pow(256, i));// 当前节点有多少子项
    }
    index += temp_len;//temp_len指的是数据长度字节  1-2-3   后续当前节点的数据部分

    msg.DataType = dt;
    msg.Length = len;
    if (dt == DataType.LIST)
    {
        if (len > 0)
        {
            //Message mm = this.GetMessage(msg_bytes.Skip(index).ToArray());
            //msg.SubNode.Add(mm);
            GetMessage(msg_bytes.Skip(index).ToArray(), msg, len);
        }
    }
    else
    {
        msg.Data = msg_bytes.Skip(index).Take(len).ToArray();
        index += len;
    }

    // 如果后面还有数据项  需要进行下一步的GetMessage
    // 检查  List的子项数据是否已满
    return msg;
}

private void GetMessage(byte[] msg_bytes, Message parent_list, int count)
{
    // 下一个字节起始
    int index = 0;
    while (count-- > 0)
    {
        #region 
        Message msg = new Message();
        // 第一个字节,表示类型和长度字节数
        byte b = msg_bytes[index];
        // 取出类型
        byte type = (byte)(b & 0b11111100);
        DataType dt = (DataType)type;
        // 取出长度字节数
        byte temp_len = (byte)(b & 0b00000011);/// 后面有多个字节表示数据长度
        index++;

        // 这里表示数据字节数   可能最多3个字节
        byte[] len_bytes = msg_bytes.Skip(index).Take(temp_len).ToArray();
        Array.Reverse(len_bytes);   // 如:01 02 03  -> 03 02 01
        int len = 0;  // 4byte
                      // 这里需要考虑三种不同长度字节的转换情况
        for (int i = 0; i < temp_len; i++)
        {
            // 03  02 512    515   01 *256*256+515
            // 字节数
            len += (int)(len_bytes[i] * Math.Pow(256, i));// 当前节点有多少子项
        }
        index += temp_len;//temp_len指的是数据长度字节  1-2-3   后续当前节点的数据部分

        msg.DataType = dt;
        msg.Length = len;
        if (dt == DataType.LIST)
        {
            if (len > 0)
            {
                GetMessage(msg_bytes.Skip(index).ToArray(), msg, len);
            }
        }
        else
        {
            msg.Data = msg_bytes.Skip(index).Take(len).ToArray();
            index += len;
        }
        #endregion
        parent_list.SubNode.Add(msg);
    }
}

这里关键是理解GetMessage方法的封装

5. 封装请求方法

前面我们实现了S1F1指令,而其他的指令求的过程跟S1F1差不多,大部分只是数据格式不一样。我们现在封装一个Request方法,用来抽象请求过程,进而快速实现其他协议的请求。

private Message Request(byte stream, byte function, Message arg)
{
    byte[] system_bytes = new byte[4] {
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255)
    };
    List<byte> req_bytes = new List<byte> {
        0x00,0x00,0x00,0x0A,
        _deviceId[0],_deviceId[1],   // DeviceID
        stream,function,
        0x00,
        0x00,
        system_bytes[0],
        system_bytes[1],
        system_bytes[2],
        system_bytes[3]
    };

    // 添加Message信息
    if (arg != null)
    {
        List<byte> data_bytes = MessageToBytes(arg);

        int total_len = data_bytes.Count + 10;
        byte[] total_len_bytes = BitConverter.GetBytes(total_len);
        Array.Reverse(total_len_bytes);
        for (int i = 0; i < 4; i++)
        {
            req_bytes[i] = total_len_bytes[i];
        }

        req_bytes.AddRange(data_bytes);
    }

    byte[] resp = SendAndReceive(req_bytes.ToArray());

    // 所有主动发送的Function都是奇数   被动响应的Function都偶数
    if ((resp[2] | 0x80) != stream || resp[3] != function + 1)
    {
        if (resp[2] == 9)
            if (Status.Errors.ContainsKey(resp[3]))
                throw new Exception(Status.Errors[resp[3]]);
            else
                throw new Exception("响应异常");
    }
    byte[] sb = resp.Skip(6).Take(4).ToArray();
    if (!sb.SequenceEqual(system_bytes))
        throw new Exception("响应异常");

    // 获取Message Text
    byte[] msg_bytes = resp.Skip(10).ToArray();

    Message message = this.GetMessage(msg_bytes);
    return message;
}

private List<byte> MessageToBytes(Message arg)
{
    List<byte> bytes = new List<byte>();

    int len_count = (int)Math.Ceiling(arg.Length * 1.0 / 256);
    if (len_count > 3) throw new Exception("传输数据长度有误,请确认");

    byte data_type = (byte)((byte)arg.DataType | len_count);

    byte[] len_bytes = BitConverter.GetBytes(arg.Length);
    Array.Reverse(len_bytes);

    bytes.Add(data_type);// 类型
    for (int i = 4 - len_count; i < 4; i++)
    {
        bytes.Add(len_bytes[i]);// 长度
    }

    if (arg.DataType == DataType.LIST)
    {
        foreach (var node in arg.SubNode)
        {
            bytes.AddRange(MessageToBytes(node));
        }
    }
    else
    {
        bytes.AddRange(arg.Data);
    }

    return bytes;
}

6. 其他指令实现

public Message S1F1()
{
    return this.Request(_deviceId, 0x81, 1, null);
}

public Message S1F3(UInt32 SVID)
{
    Message message = new Message();
    message.DataType = DataType.LIST;
    message.Length = 1;

    byte[] data_bytes = BitConverter.GetBytes(SVID);
    Array.Reverse(data_bytes);
    message.SubNode.Add(new Message
                        {
                            DataType = DataType.UINT_4,
                            Length = 4,
                            Data = data_bytes
                        });

    return this.Request(_deviceId, 0x81, 3, message);
}

public Message S1F13()
{
    Message msg = new Message();
    msg.DataType = DataType.LIST;
    msg.Length = 0;

    return this.Request(_deviceId, 0x81, 13, msg);
}

public Message S1F15()
{
    return this.Request(_deviceId, 0x81, 15, null);
}

public Message S1F17()
{
    return this.Request(_deviceId, 0x81, 17, null);
}

// 这只是一个案例,具体实现未做测试
public Message S6F11(UInt32 DATAID, UInt32 CEID, UInt32 RPTID, string v)
{
    Message msg = new Message();
    msg.DataType = DataType.LIST;
    msg.Length = 3;

    msg.SubNode.Add(new Message
                    {
                        DataType = DataType.UINT_4,
                        Length = 4,
                        Data = BitConverter.GetBytes(DATAID), // 这里需要顺序颠倒
                    });
    msg.SubNode.Add(new Message
                    {
                        DataType = DataType.UINT_4,
                        Length = 4,
                        Data = BitConverter.GetBytes(CEID), // 这里需要顺序颠倒
                    });

    Message msg2 = new Message();
    msg2.DataType = DataType.LIST;
    msg2.Length = 1;
    msg2.SubNode.Add(new Message()
                     {
                         DataType = DataType.LIST,
                         Length = 2,
                         SubNode = new List<Message>()
                         {
                             new Message{
                                 DataType = DataType.UINT_4,
                                 Length = 4,
                                 Data = BitConverter.GetBytes(RPTID)
                             },
                             new Message{
                                 DataType= DataType.LIST,
                                 Length = 1,
                                 SubNode = new List<Message>(){
                                     new Message{
                                         DataType=DataType.ASCII,
                                         Length=v.Length,
                                         Data=Encoding.UTF8.GetBytes(v)
                                     }
                                 }
                             }
                         }
                     });

    msg.SubNode.Add(msg2);

    return this.Request(_deviceId, 0x86, 11, msg);
}

7. 心跳与消息接收

  • 封装消息接收逻辑
CancellationTokenSource cts = new CancellationTokenSource();
ConcurrentDictionary<string, MessageMode> _messageDic = new ConcurrentDictionary<string, MessageMode>();

private byte[] GetReply(string system_bytes, int t)
{
    byte[] resp = null;// = SendAndReceive(select_bytes);
    Task task = new Task(() =>
    {
        while (!cts.IsCancellationRequested)
        {
            MessageMode mm = null;

            if (_messageDic.TryRemove(system_bytes, out mm))
            {
                resp = mm.MessageBytes;
                break;
            }
        }
    }, cts.Token);
    task.Start();
    bool state = task.Wait(t);

    if (!state)
    {
        // 说明超时了
        throw new Exception("接收响应超时了");
    }

    // S1F2  -  DeviceId
    if (resp == null) throw new Exception("响应异常");

    if (resp[2] == 9)
    {
        if (Status.Errors.ContainsKey(resp[3]))
            throw new Exception(Status.Errors[resp[3]]);
        else
            throw new Exception("响应异常");
    }

    byte[] sb = resp.Skip(6).Take(4).ToArray();
    if (system_bytes != string.Join("", sb.Select(b => b.ToString("X2"))))
        throw new Exception("响应异常:SystemBytes");

    return resp;
}

_messageDic 用来临时存放所有接收到的消息,然后通过system_bytes来读取。在超时范围内读取对应的数据了,就从字典中取出来并移除。

  • 封装心跳逻辑
// 心跳
private void LinkTest()
{
    Task.Run(async () =>
    {
        while (!cts.IsCancellationRequested)
        {
            await Task.Delay(Status.BeatInterval);

            try
            {
                byte[] system_bytes = new byte[4] {
                        (byte)rand.Next(0,255),
                        (byte)rand.Next(0,255),
                        (byte)rand.Next(0,255),
                        (byte)rand.Next(0,255)
                    };
                byte[] link_bytes = new byte[] {
                        0x00,0x00,0x00,0x0A,// 数据按大端处理
                        0xFF,0xFF,
                        0x00,0x00,0x00,
                        0x05,
                        system_bytes[0],
                        system_bytes[1],
                        system_bytes[2],
                        system_bytes[3],
                    };
                socket.Send(link_bytes);

                byte[] resp = this.GetReply(string.Join("", system_bytes.Select(b => b.ToString("X2"))), Status.T3);

                if (resp[5] != 6)
                    throw new Exception("响应异常");
            }
            catch { }
        }
    }, cts.Token);
}
  • 封装被动响应回复逻辑
// 被动指令
private void PassiverResponse()
{
    Task.Run(() =>
             {
                 while (!cts.IsCancellationRequested)
                 {
                     byte[] len_resp = new byte[4];
                     int count = socket.Receive(len_resp, 0, 4, SocketFlags.None);
                     if (count == 0) continue;

                     Array.Reverse(len_resp);
                     int len = BitConverter.ToInt32(len_resp, 0);

                     byte[] header_resp = new byte[10];
                     count = socket.Receive(header_resp, 0, 10, SocketFlags.None);
                     if (count == 0) continue;

                     byte[] body_resp = new byte[len - 10];
                     if (len - 10 > 0)
                     {
                         count = socket.Receive(body_resp, 0, len - 10, SocketFlags.None);
                         if (count == 0) continue;
                     }

                     // 解析出是哪一个指令的响应   还是具体的主动指令
                     byte[] sb = header_resp.Skip(6).Take(4).ToArray();
                     if (header_resp[3] % 2 == 0 || (header_resp[2] == 0 && header_resp[3] == 0)) // 响应结果
                     {
                         List<byte> msg_bytes = header_resp.ToList();
                         msg_bytes.AddRange(body_resp);
                         _messageDic.TryAdd(string.Join("", sb.Select(b => b.ToString("X2"))), new MessageMode
                                            {
                                                MessageBytes = msg_bytes.ToArray(),
                                            });
                         Console.WriteLine(string.Join(" ", msg_bytes.Select(b => b.ToString("X2"))));
                     }
                     else// 被动指令
                     {
                         // S1F2
                         byte stream = header_resp[2];  // 0x81
                         byte function = (byte)(header_resp[3] + 1);

                         if (stream < 0x80)
                         {

                         }
                         else
                         {
                             stream &= 0b01111111;

                             byte[] system_bytes = sb;

                             // 1-2
                             //Message msg = this.S1F2();
                             //Request(stream, function, msg);

                             // 1-14
                             //msg = this.S1F14();
                             //Request(stream, function, msg);

                             MethodInfo mi = this.GetType().GetMethod($"S{stream}F{function}", BindingFlags.NonPublic | BindingFlags.Instance);
                             if (mi == null)
                             {
                                 // 相关功能不支持
                                 return;
                             }
                             Message msg = mi.Invoke(this, null) as Message;
                             // DeviceId
                             this.Request(header_resp.Take(2).ToArray(),
                                          stream, function,
                                          msg,
                                          header_resp.Skip(6).Take(4).ToArray(),
                                          true);
                         }
                     }
                 }
             });
}
  • 修改连接方法,并执行心跳
public void Connect(string host, int port)
{
    socket.Connect(host, port); // 3次握手
    this.PassiverResponse();    // 被动指令

    // 发送Select状态请求
    byte[] system_bytes = new byte[4] {
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255),
        (byte)rand.Next(0,255)
    };
    byte[] select_bytes = new byte[] {
        0x00,0x00,0x00,0x0A,// 数据按大端处理
        0xFF,0xFF,
        0x00,0x00,0x00,
        0x01,
        system_bytes[0],
        system_bytes[1],
        system_bytes[2],
        system_bytes[3],
    };
    socket.Send(select_bytes);

    byte[] resp = this.GetReply(string.Join("", system_bytes.Select(b => b.ToString("X2"))), Status.T6);
    if (resp[5] != 2)
        throw new Exception("响应异常");

    _deviceId = resp.Take(2).ToArray();

    // 执行心跳
    this.LinkTest();
}
  • 实现被动指令
public Message S1F2()
{
    Message msg = new Message()
    {
        DataType = DataType.LIST,
        Length = 0
    };
    return msg;
}

public Message S1F14()
{
    // Host的响应消息结构
    Message msg = new Message()
    {
        DataType = DataType.LIST,
        Length = 2
    };
    msg.SubNode.Add(new Message
                    {
                        DataType = DataType.BINARY,
                        Length = 1,
                        Data = new byte[] { 0x00 }
                    });
    msg.SubNode.Add(new Message
                    {
                        DataType = DataType.LIST,
                        Length = 0
                    });
    return msg;
}

8. 测试

static void HsmsTest()
{
    HSMS hsms = new HSMS();

    hsms.MDLN = "Xiaosuo";
    hsms.SOFTREV = "V0.0.1";

    hsms.Connect("192.168.0.1", 5000);

    //Message message = hsms.S1F1();
    //Console.WriteLine(Encoding.UTF8.GetString(message.SubNode[0].Data));

    //Message message = hsms.S1F13();

    //Message message = hsms.S1F3(123);

    //Message message = hsms.S1F15();

    //Message message = hsms.S1F17();

    Message message = hsms.S6F11(1, 2, 3, "Hello");// 做为Equpment
}