Unity使用Modbus协议

发布于:2024-08-08 ⋅ 阅读:(166) ⋅ 点赞:(0)

最近一直在工业领域干活,学习下Modbus协议,这里做个记录,理解不对的地方希望大佬指出修正。
一、先上测试工具和Unity脚本。
1.测试工具使用的 Modsim32
2.Unity测试脚本如下

/*
· 0x01:读线圈
· 0x05:写单个线圈
· 0x0F:写多个线圈
· 0x02:读离散量输入
· 0x04:读输入寄存器
· 0x03:读保持寄存器
· 0x06:写单个保持寄存器
· 0x10:写多个保持寄存器

*/

using Modbus.Device;
using Modbus.Extensions.Enron;
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Unity.Collections.LowLevel.Unsafe;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
/// <summary>
/// Modbus Tcp/IP
/// </summary>
public class TestModBus : MonoBehaviour
{
    [Header("设备ID")]
    public byte deviceId;
    [Header("读取起始位置")]
    public ushort readStart;
    [Header("读取的长度")]
    public ushort readLength;
    [Header("写入的起始位置")]
    public ushort writeStart;
    [Header("写入的数据")]
    public ushort[] udata = new ushort[] { 100 };
    public bool[] coils;
    [Header("区域类型")]
    public ModbusPointType modbusPointType;

    public ModbusMaster modbusIpMaster;
    public TcpClient tcpClient;
    [Header("连接IP")]
    IPAddress address = new IPAddress(new byte[] { 127, 0, 0, 1 });
    [Header("连接端口")]
    public int port = 502;

    // Start is called before the first frame update
    void Start()
    {
        if (Connect(address.ToString(), port))
        {
            Debug.Log("连接成功");
        }
        else
        {
            Debug.Log("连接失败");
        }
    }
    public bool Connect(string ip, int port)
    {
        try
        {
            tcpClient = new TcpClient(ip, port);
            tcpClient.SendTimeout = 1;
            modbusIpMaster = ModbusIpMaster.CreateIp(tcpClient);
            return true;
        }
        catch (Exception ex)
        {
            if (tcpClient!=null)
            {
                tcpClient.Close();
            }

            Debug.LogError(ex.Message);
            return false;
        }
    }

    private void OnDestroy()
    {
        if (tcpClient!=null)
        {
            tcpClient.Close();
        }
    }
    

    /// 10进制转16进制 
    private byte GetHex(string msg)
    {
        byte hex = Convert.ToByte(msg);
        return hex;
    }
    ///16进制转10进制 
    public int GetDex(string msg)
    {
        int res = Convert.ToInt32(msg, 16);
        return res;
    }
    //退出的时候关闭连接
    private void OnApplicationQuit()
    {
        if (tcpClient!=null)
        {
            tcpClient.Close();
        }
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            WriteData(modbusPointType);
            //WriteMultipleRegisters(设备地址,寄存器起始地址, 要写入的值)  
            //modbusIpMaster.WriteMultipleRegisters(0x02, star, udata);
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            ReadData(modbusPointType);
            //ReadHoldingRegisters(设备地址, 寄存器起始地址, 读取数量);  读取多个寄存器
            //ushort[] msg = modbusIpMaster.ReadHoldingRegisters(0x01, 1, 0x06);

        }
    }
    /// <summary>
    /// (设备地址,寄存器起始地址, 要写入的值) 
    /// </summary>
    /// <param name="type"></param>
    private void WriteData(ModbusPointType type)
    {
        switch (type)
        {
            case ModbusPointType.Coil:
                modbusIpMaster.WriteMultipleCoils(deviceId, writeStart, coils);
                for (int i = 0; i < coils.Length; i++)
                {
                    Debug.LogFormat("Coil, {0},写入数据={1}", i, coils[i]);
                }
                break;
            case ModbusPointType.HoldRegister:
                modbusIpMaster.WriteMultipleRegisters(deviceId, writeStart, udata);
                for (int i = 0; i < udata.Length; i++)
                {
                    Debug.LogFormat("HoldRegister, {0},写入数据={1}", i, udata[i]);
                }
                break;
            default:
                break;
        }
    }
    /// <summary>
    /// (设备地址, 寄存器起始地址, 读取数量)
    /// </summary>
    /// <param name="type"></param>
    private void ReadData(ModbusPointType type)
    {
        bool[] msgBool;
        ushort[] msgShort;
        switch (type)
        {
            case ModbusPointType.Coil:
                msgBool = modbusIpMaster.ReadCoils(deviceId, readStart, readLength);
                for (int i = 0; i < msgBool.Length; i++)
                {
                    Debug.LogFormat("Coil, {0},接收到的数据={1}", i,msgBool[i]);
                }
                return;
            case ModbusPointType.Input:
                msgBool = modbusIpMaster.ReadInputs(deviceId, readStart, readLength);
                for (int i = 0; i < msgBool.Length; i++)
                {
                    Debug.LogFormat("Input, {0},接收到的数据={1}", i, msgBool[i]);
                }
                break;
            case ModbusPointType.InputRegister:
                msgShort = modbusIpMaster.ReadInputRegisters(deviceId, readStart, readLength);
                for (int i = 0; i < msgShort.Length; i++)
                {
                    Debug.LogFormat("InputRegister, {0},接收到的数据={1}", i, msgShort[i]);
                }
                break;
            case ModbusPointType.HoldRegister:
                msgShort = modbusIpMaster.ReadHoldingRegisters(deviceId, readStart, readLength);
                for (int i = 0; i < msgShort.Length; i++)
                {
                    Debug.LogFormat("HoldRegister, {0},接收到的数据={1}", i, msgShort[i]);
                }
                break;
            default:
                break;
        }
    }
}

public class IPTest
{
    public string ip;
}

public enum ModbusPointType
{
    Coil,//线圈  发送的二进制格式 可读写
    Input,//离散量输入 发送的二进制格式 只读
    InputRegister,//输入寄存器 发送的ushort格式 只读
    HoldRegister//保持寄存器  发送的ushort格式 可读写
}


二、连接创建。
请添加图片描述
1.创建连接,上方菜单栏点击连接设置,选择Modbus链接,其他的猜测是串口通信测试,没做具体研究。
2.大概百度学习了下,我理解的modbus是通过TCP链接使用的一种通信协议。
3.代码中直接使用的SocketTCP相关的代码创建的链接,只不过我之前使用的都是私有协议,自己定义消息协议,消息头、消息体之类的,modbus是大厂制定的一种通用消息协议,被沿用至今。
请添加图片描述
三、四种Point Type,代码中已经注释写明。
1.个人理解是四种数据类型,两个都可读,两个可写,可传输的值不同,适配不同的使用需求。
2.经过测试,向Coil中写入数据,再从Input中可读取到上一步写入的数据。
3.以下是对实际应用猜测,也是学习中不太确定的地方,希望有专业人士指导。
理解实际应用中比如报警状态识别功能,由硬件设备向Coil中写入报警状态,然后显示层可以从Input中读取对应的位置,这样避免显示层对报警状态做出修改,只有硬件本身可以设置报警状态,所以采用二进制的数据格式就可以了。
HoldRegister可以存储更多的数据,可用来记录复杂的操作,比如操作流程,或者可编程式的运行规则,同样是HoldRegister写入后,InputRegister提供了只读的访问方法,可以访问到HoldRegister写入的数据。
请添加图片描述
四、数据发送读取。
代码中已经封装读写功能,基本思路就是选择设备id,start起始位置,然后对应设置写入的数值,数值长度不能超过测试软件设置的数据结构的count大小。这个就类似socketTCP中拼协议,从索引第几位开始,写入多长的数据。


网站公告

今日签到

点亮在社区的每一天
去签到