• Modbus动态链接库供多语言使用 | Go


    Modbus协议控制动态链接库

    应用场景

    基于各门语言都有各自的modbus协议库,且良莠不齐,而且在具体的框架下可能存在版本依赖问题,
    而且对modbus协议存在比较多的细节处理,可以查看modbus slave、或者modbus poll中相关的配置可知,
    数据类型对应读写寄存器个数、大小端的处理等等细节,所以将以常用的场景下的配置编写动态链接库供其他语言调用。
    

    编程语言

    因为我不会写C语言,这里使用Go语言编写代码,以C函数分享库的模式编译成动态链接库
    将以Python与Node.js调用的示例演示

    辅助工具下载地址

    Modbus Poll: 激活码(仅供学习 5A5742575C5D10) -> 模拟主站

    链接: https://pan.baidu.com/s/1Sk2m6HWTU0-hE82-BxPvKA 提取码: 1fwn 
    

    Modbus Slave: 激活码(仅供学习 5455415451475662) -> 模拟从站

    链接: https://pan.baidu.com/s/137w1-2gGQ3bETvbyBefNQg 提取码: 9433 
    

    编写Go代码示例 使用Cgo引入C函数

    • Modbus TCP示例
    package main
    
    /*
    #include 
    */
    import (
    	"C"
    	"bytes"
    	"encoding/binary"
    	"fmt"
    	"log"
    	"math"
    	"time"
    
    	"github.com/goburrow/modbus"
    )
    import "strings"
    
    var handler *modbus.TCPClientHandler
    
    //export Connect
    func Connect(ip *C.char, port C.int) C.int {
    	log.Printf("Connect ...")
    	handler = modbus.NewTCPClientHandler(fmt.Sprintf("%s:%d", C.GoString(ip), int(port)))
    	handler.Timeout = 250 * time.Millisecond
    	err := handler.Connect()
    	if err != nil {
    		log.Printf("Connect error: %v", err)
    		return -1
    	}
    	return 0
    }
    
    //export Close
    func Close() C.int {
    	log.Printf("Close ...")
    	err := handler.Close()
    	if err != nil {
    		log.Printf("Close error: %v", err)
    		return -1
    	}
    	return 0
    }
    
    //ReadRegister 一般错误返回 -1 连接失败返回 -3
    //export ReadRegister
    func ReadRegister(slaveId C.int, address C.int, dataType *C.char, data *C.double) C.int {
    	var results []byte
    	var err error
    
    	handler.SlaveId = byte(slaveId)
    
    	client := modbus.NewClient(handler)
    
    	dt := C.GoString(dataType)
    
    	switch dt {
    	case "int16":
    		results, err = client.ReadInputRegisters(uint16(address), 1)
    	case "int32", "float":
    		results, err = client.ReadInputRegisters(uint16(address), 2)
    	case "int64", "double":
    		results, err = client.ReadInputRegisters(uint16(address), 4)
    	}
    
    	if err != nil {
    		switch {
    		case strings.Contains(err.Error(), "connection"):
    			log.Println("Connection error:", err)
    			return -3
    		case strings.Contains(err.Error(), "timeout"):
    			log.Println("Timeout error:", err)
    			return -1
    		default:
    			log.Println("Other error:", err)
    			return -1
    		}
    	}
    
    	var value float64
    	switch dt {
    	case "int16":
    		var intValue int16
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "int32":
    		var intValue int32
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "float":
    		var floatValue float32
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &floatValue)
    		value = float64(floatValue)
    	case "int64":
    		var intValue int64
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "double":
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &value)
    	}
    
    	if err != nil {
    		log.Println(err)
    		return -1
    	}
    
    	*data = C.double(value)
    	return 0
    }
    
    //ReadHoldingRegister 一般错误返回 -1 连接失败返回 -3
    //export ReadHoldingRegister
    func ReadHoldingRegister(slaveId C.int, address C.int, dataType *C.char, data *C.double) C.int {
    	var results []byte
    	var err error
    
    	handler.SlaveId = byte(slaveId)
    
    	client := modbus.NewClient(handler)
    
    	dt := C.GoString(dataType)
    	// startTime := time.Now()
    	switch dt {
    	case "int16":
    		results, err = client.ReadHoldingRegisters(uint16(address), 1)
    	case "int32", "float":
    		results, err = client.ReadHoldingRegisters(uint16(address), 2)
    	case "int64", "double":
    		results, err = client.ReadHoldingRegisters(uint16(address), 4)
    	}
    	// endTime := time.Now()
    
    	// // 计算时间差
    	// duration := endTime.Sub(startTime)
    	// fmt.Printf("Takes: %v\n", duration)
    	if err != nil {
    		switch {
    		case strings.Contains(err.Error(), "connection"):
    			log.Println("Connection error:", err)
    			return -3
    		case strings.Contains(err.Error(), "timeout"):
    			log.Println("Timeout error:", err)
    			return -1
    		default:
    			log.Println("Other error:", err)
    			return -1
    		}
    	}
    
    	var value float64
    	switch dt {
    	case "int16":
    		var intValue int16
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "int32":
    		var intValue int32
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "float":
    		var floatValue float32
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &floatValue)
    		value = float64(floatValue)
    	case "int64":
    		var intValue int64
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "double":
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &value)
    	}
    
    	if err != nil {
    		log.Println(err)
    		return -1
    	}
    
    	*data = C.double(value)
    	return 0
    }
    
    //export WriteRegister
    func WriteRegister(slaveId C.int, address C.int, dataType *C.char, value C.double) C.int {
    	var err error
    
    	handler.SlaveId = byte(slaveId)
    	client := modbus.NewClient(handler)
    
    	dt := C.GoString(dataType)
    
    	switch dt {
    	case "int16":
    		var intValue int16 = int16(value)
    		_, err = client.WriteSingleRegister(uint16(address), uint16(intValue))
    	case "int32":
    		var intValue int32 = int32(value)
    		results := make([]byte, 4)
    		binary.BigEndian.PutUint32(results, uint32(intValue))
    		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
    	case "float":
    		var floatValue float32 = float32(value)
    		results := make([]byte, 4)
    		binary.BigEndian.PutUint32(results, math.Float32bits(floatValue))
    		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
    	case "int64":
    		var intValue int64 = int64(value)
    		results := make([]byte, 8)
    		binary.BigEndian.PutUint64(results, uint64(intValue))
    		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
    	case "double":
    		var doubleValue float64 = float64(value)
    		results := make([]byte, 8)
    		binary.BigEndian.PutUint64(results, math.Float64bits(doubleValue))
    		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
    	default:
    		return -1
    	}
    
    	if err != nil {
    		switch {
    		case strings.Contains(err.Error(), "connection"):
    			log.Println("Connection error:", err)
    			return -3
    		case strings.Contains(err.Error(), "timeout"):
    			log.Println("Timeout error:", err)
    			return -1
    		default:
    			log.Println("Other error:", err)
    			return -1
    		}
    	}
    	return 0
    }
    
    func main() {}
    
    • Modbus RTU示例
    package main
    
    /*
    #include 
    */
    import (
    	"C"
    	"bytes"
    	"encoding/binary"
    	"log"
    	"math"
    	"time"
    
    	"github.com/goburrow/modbus"
    )
    import "strings"
    
    var handler *modbus.RTUClientHandler
    
    //export Connect
    func Connect(port *C.char, baudrate C.int) C.int {
    	log.Printf("Connect ...")
    	handler = modbus.NewRTUClientHandler(C.GoString(port))
    	handler.BaudRate = int(baudrate)
    	handler.DataBits = 8
    	handler.Parity = "N"
    	handler.StopBits = 1
    	handler.Timeout = 250 * time.Millisecond
    	err := handler.Connect()
    	if err != nil {
    		log.Printf("Connect error: %v", err)
    		return -1
    	}
    	return 0
    }
    
    //export Close
    func Close() C.int {
    	log.Printf("Close ...")
    	err := handler.Close()
    	if err != nil {
    		log.Printf("Close error: %v", err)
    		return -1
    	}
    	return 0
    }
    
    //ReadRegister 一般错误返回 -1 连接失败返回 -3
    //export ReadRegister
    func ReadRegister(slaveId C.int, address C.int, dataType *C.char, data *C.double) C.int {
    	var results []byte
    	var err error
    
    	handler.SlaveId = byte(slaveId)
    
    	client := modbus.NewClient(handler)
    
    	dt := C.GoString(dataType)
    
    	switch dt {
    	case "int16":
    		results, err = client.ReadInputRegisters(uint16(address), 1)
    	case "int32", "float":
    		results, err = client.ReadInputRegisters(uint16(address), 2)
    	case "int64", "double":
    		results, err = client.ReadInputRegisters(uint16(address), 4)
    	}
    
    	if err != nil {
    		switch {
    		case strings.Contains(err.Error(), "connection"):
    			log.Println("Connection error:", err)
    			return -3
    		case strings.Contains(err.Error(), "timeout"):
    			log.Println("Timeout error:", err)
    			return -1
    		default:
    			log.Println("Other error:", err)
    			return -1
    		}
    	}
    
    	var value float64
    	switch dt {
    	case "int16":
    		var intValue int16
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "int32":
    		var intValue int32
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "float":
    		var floatValue float32
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &floatValue)
    		value = float64(floatValue)
    	case "int64":
    		var intValue int64
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "double":
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &value)
    	}
    
    	if err != nil {
    		log.Println(err)
    		return -1
    	}
    
    	*data = C.double(value)
    	return 0
    }
    
    //ReadHoldingRegister 一般错误返回 -1 连接失败返回 -3
    //export ReadHoldingRegister
    func ReadHoldingRegister(slaveId C.int, address C.int, dataType *C.char, data *C.double) C.int {
    	var results []byte
    	var err error
    
    	handler.SlaveId = byte(slaveId)
    
    	client := modbus.NewClient(handler)
    
    	dt := C.GoString(dataType)
    	// startTime := time.Now()
    	switch dt {
    	case "int16":
    		results, err = client.ReadHoldingRegisters(uint16(address), 1)
    	case "int32", "float":
    		results, err = client.ReadHoldingRegisters(uint16(address), 2)
    	case "int64", "double":
    		results, err = client.ReadHoldingRegisters(uint16(address), 4)
    	}
    	// endTime := time.Now()
    
    	// // 计算时间差
    	// duration := endTime.Sub(startTime)
    	// fmt.Printf("Takes: %v\n", duration)
    	if err != nil {
    		switch {
    		case strings.Contains(err.Error(), "connection"):
    			log.Println("Connection error:", err)
    			return -3
    		case strings.Contains(err.Error(), "timeout"):
    			log.Println("Timeout error:", err)
    			return -1
    		default:
    			log.Println("Other error:", err)
    			return -1
    		}
    	}
    
    	var value float64
    	switch dt {
    	case "int16":
    		var intValue int16
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "int32":
    		var intValue int32
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "float":
    		var floatValue float32
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &floatValue)
    		value = float64(floatValue)
    	case "int64":
    		var intValue int64
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &intValue)
    		value = float64(intValue)
    	case "double":
    		err = binary.Read(bytes.NewReader(results), binary.BigEndian, &value)
    	}
    
    	if err != nil {
    		log.Println(err)
    		return -1
    	}
    
    	*data = C.double(value)
    	return 0
    }
    
    //export WriteRegister
    func WriteRegister(slaveId C.int, address C.int, dataType *C.char, value C.double) C.int {
    	var err error
    
    	handler.SlaveId = byte(slaveId)
    	client := modbus.NewClient(handler)
    
    	dt := C.GoString(dataType)
    
    	switch dt {
    	case "int16":
    		var intValue int16 = int16(value)
    		_, err = client.WriteSingleRegister(uint16(address), uint16(intValue))
    	case "int32":
    		var intValue int32 = int32(value)
    		results := make([]byte, 4)
    		binary.BigEndian.PutUint32(results, uint32(intValue))
    		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
    	case "float":
    		var floatValue float32 = float32(value)
    		results := make([]byte, 4)
    		binary.BigEndian.PutUint32(results, math.Float32bits(floatValue))
    		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
    	case "int64":
    		var intValue int64 = int64(value)
    		results := make([]byte, 8)
    		binary.BigEndian.PutUint64(results, uint64(intValue))
    		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
    	case "double":
    		var doubleValue float64 = float64(value)
    		results := make([]byte, 8)
    		binary.BigEndian.PutUint64(results, math.Float64bits(doubleValue))
    		_, err = client.WriteMultipleRegisters(uint16(address), uint16(len(results)/2), results)
    	default:
    		return -1
    	}
    
    	if err != nil {
    		switch {
    		case strings.Contains(err.Error(), "connection"):
    			log.Println("Connection error:", err)
    			return -3
    		case strings.Contains(err.Error(), "timeout"):
    			log.Println("Timeout error:", err)
    			return -1
    		default:
    			log.Println("Other error:", err)
    			return -1
    		}
    	}
    	return 0
    }
    
    func main() {}
    

    编译动态链接库

    Go语言支持交叉编译成各个平台的二进制运行程序,但是Cgo不支持,可以去寻找第三方库去处理这个问题,这里选择用Docker容器

    • 编译命令

    windows下是modbus.dll,Linux下是 modbus.so,Macos下是 modbus.dylib

    go build -o modbus.so -buildmode=c-shared main.go
    
    • 其他平台编译,比如树莓派,选择使用docker容器
    这里找一个 arm32v7 的docker容器,可以通过 docker search arm32v7的方式去搜索,我是试了几个选了个node版本的容器,
    不同版本里面的 GLIBC 可能不一样,需要先查看你的树莓派上 GLIBC版本,通过命令 strings /lib/*/libc.so.* | grep GLIBC,
    根据版本我选择了node v16的版本下载镜像
    
    • 下载镜像、创建容器

    docker run -itd --name raspberry arm32v7/node:16

    • 进入容器、安装go环境
    wget -e http_proxy=127.0.0.1:10809  https://studygolang.com/dl/golang/go1.17.linux-armv6l.tar.gz
    tar -zxvf go1.17.linux-armv6l.tar.gz go/
    mv go /usr/local/go
    # 安装vim
    apt-get update
    apt-get install vim -y
    # 加入系统环境
    vim /etc/profile # 加入内容到最后一行 export PATH=$PATH:/usr/local/go/bin
    source /etc/profile # 每次进入容器都要执行一次,要是没有找到go命令的话
    
    • 将Go代码文件拷贝至容器
    docker cp modbus_dir raspberry:/  # 将modbus_dir拷贝至容器raspberry的 / 目录下
    
    • 进入容器编译动态链接库
    docker exec -it raspberry /bin/bash
    cd modbus_dir
    go build -o modbus.so -buildmode=c-shared main.go
    
    • 将编译后的动态链接库拷贝到宿主机
    exit # 退出容器
    docker cp raspberry:/modbus_dir/modbus.so ./  # 拷贝至当前目录
    

    应用示例

    • Python

    modbus tcp示例

    import ctypes
    import platform
    
    # 加载共享库
    print(platform.system())
    if platform.system() == "Windows":
      modbus = ctypes.CDLL('./modbus.dll')
    elif platform.system() == "Darwin":
      modbus = ctypes.CDLL('./modbus.dylib')
    else:  # 树莓派
      modbus = ctypes.CDLL('./modbus.so')
    
    # 定义函数参数类型和返回类型
    modbus.ReadRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)]
    modbus.ReadRegister.restype = ctypes.c_int
    
    modbus.WriteRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_double]
    modbus.WriteRegister.restype = ctypes.c_int
    
    modbus.ReadHoldingRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)]
    modbus.ReadHoldingRegister.restype = ctypes.c_int
    
    modbus.Connect.argtypes = [ctypes.c_char_p, ctypes.c_int]
    modbus.Connect.restype = ctypes.c_int
    
    modbus.Close.argtypes = []
    modbus.Close.restype = ctypes.c_int
    
    # 调用ReadRegister函数
    modbus.Connect(b'192.168.1.138', 502)
    
    # 写控制寄存器
    for i in range(50):
      data = ctypes.c_double()
      result = modbus.WriteRegister(1, 30, b'float', 123.45 + i)
      if result == -3:
        print('Connect error.')
      if result == 0:
        print('Write success.')
    
    # 读控制寄存器 | 读输入寄存器时改成 ReadRegister
    for i in range(50):
      data = ctypes.c_double()
      result = modbus.ReadHoldingRegister(1, 30, b'int16', ctypes.byref(data))
      print('Read Result:', result, data.value)
    modbus.Close()
    

    modbus rtu示例

    import ctypes
    import platform
    
    # 加载共享库
    print(platform.system())
    if platform.system() == "Windows":
      modbus = ctypes.CDLL('./modbus-rtu.dll')
    elif platform.system() == "Darwin":
      modbus = ctypes.CDLL('./modbus-rtu.dylib')
    else:  # 树莓派
      modbus = ctypes.CDLL('./modbus-rtu.so')
    
    # 定义函数参数类型和返回类型
    modbus.ReadRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)]
    modbus.ReadRegister.restype = ctypes.c_int
    
    modbus.WriteRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_double]
    modbus.WriteRegister.restype = ctypes.c_int
    
    modbus.ReadHoldingRegister.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_double)]
    modbus.ReadHoldingRegister.restype = ctypes.c_int
    
    modbus.Connect.argtypes = [ctypes.c_char_p, ctypes.c_int]
    modbus.Connect.restype = ctypes.c_int
    
    modbus.Close.argtypes = []
    modbus.Close.restype = ctypes.c_int
    
    # 调用ReadRegister函数
    modbus.Connect(b'COM2', 9600)
    
    # 写控制寄存器
    for i in range(50):
      data = ctypes.c_double()
      result = modbus.WriteRegister(1, 30, b'float', 123.45 + i)
      if result == -3:
        print('Connect error.')
      if result == 0:
        print('Write success.')
    
    # 读控制寄存器 | 读输入寄存器时改成 ReadRegister
    for i in range(50):
      data = ctypes.c_double()
      result = modbus.ReadHoldingRegister(1, 30, b'int16', ctypes.byref(data))
      print('Read Result:', result, data.value)
    modbus.Close()
    
    • Node.js

    这是我在electron进行软件开发过程中封装的函数
    调用顺序: DefineModbus -> connectModbus -> writeRegisterAsync/readRegisterAsync/readHoldingRegisterAsync -> connectModbus

    const ffi = require('ffi-napi');
    const ref = require('ref-napi');
    const double = ref.types.double;
    const doublePtr = ref.refType(double);
    const path = require('path');
    import {checkPort} from './net';
    
    let dllPath: string;
    
    console.log(`当前平台: ${process.platform}`)  // darwin
    
    let dllName = 'modbus.dll';
    if (process.platform === 'darwin') {
      dllName = 'modbus.dylib';
    }
    
    if (process.env.MODE === 'development') {
      dllPath = path.resolve(`extraResources/dlls/${dllName}`);
    } else {
      dllPath = path.join(process.resourcesPath, 'extraResources', 'dlls', dllName);
    }
    
    let modbus: any;
    
    const closeModbus = () => {
      return new Promise((resolve, reject) => {
        modbus.Close.async(
          (err: any, result: any) => {
            if (err) {
              reject(err);
            } else {
              resolve(result);
            }
          },
        );
      });
    };
    
    const connectModbus = (ip: string, port: number) => {
      return new Promise((resolve, reject) => {
        modbus.Connect.async(
          ip,
          port,
          (err: any, result: any) => {
            if (err) {
              reject(err);
            } else {
              resolve(result);
            }
          },
        );
      });
    };
    
    const writeRegisterAsync = (
      slaveId: number,
      startAddress: number,
      dataType: string,
      val: number,
    ) => {
      return new Promise((resolve, reject) => {
        modbus.WriteRegister.async(
          slaveId,
          startAddress,
          dataType,
          val,
          (err: any, result: any) => {
            if (err) {
              reject(err);
            } else {
              resolve(result);
            }
          },
        );
      });
    };
    
    const readRegisterAsync = (
      slaveId: number,
      startAddress: number,
      dataType: string,
      data: any,
    ) => {
      return new Promise((resolve, reject) => {
        modbus.ReadRegister.async(
          slaveId,
          startAddress,
          dataType,
          data,
          (err: any, result: any) => {
            if (err) {
              reject(err);
            } else {
              resolve(result);
            }
          },
        );
      });
    };
    
    const readHoldingRegisterAsync = (
      slaveId: number,
      startAddress: number,
      dataType: string,
      data: any,
    ) => {
      return new Promise((resolve, reject) => {
        modbus.ReadHoldingRegister.async(
          slaveId,
          startAddress,
          dataType,
          data,
          (err: any, result: any) => {
            if (err) {
              reject(err);
            } else {
              resolve(result);
            }
          },
        );
      });
    };
    
    export const ConnectModbus = async (ip: string, port: number) => {
      try {
        const result = await connectModbus(ip, port);
        return [result, 0];
      } catch (e) {
        return [-1, 0];
      }
    }
    
    export const CloseModbus = async () => {
      try {
        const result = await closeModbus();
        return [result, 0];
      } catch(e) {
        return [-1, 0];
      }
    }
    
    export const ReadByModbusTcp = async (
      slaveId: number,
      startAddress: number,
      dataType: string,
      method: string = 'Input',
    ) => {
      const data = ref.alloc(double);
      try {
        let result: any;
        if (method === 'Input') {
          result = await readRegisterAsync(slaveId, startAddress, dataType, data);
        } else {
          result = await readHoldingRegisterAsync(slaveId, startAddress, dataType, data);
        }
        return [result, data.deref() as number];
      } catch (e) {
        console.log(`e :${e}`);
        return [0, 0];
      }
    }
    
    export const WriteByModbusTcp = async (
      slaveId: number,
      startAddress: number,
      dataType: string,
      val: number,
    ) => {
      try {
        const result: any = await writeRegisterAsync(
          slaveId,
          startAddress,
          dataType,
          val,
        );
        return [result, val];
      } catch (e) {
        return [0, 0];
      }
    }
    
    export const DefineModbus = async () => {
      try {
        modbus = ffi.Library(dllPath, {
          ReadRegister: ['int', ['int', 'int', 'string', doublePtr]],
          ReadHoldingRegister: ['int', ['int', 'int', 'string', doublePtr]],
          WriteRegister: ['int', ['int', 'int', 'string', 'double']],
          Connect: ['int', ['string', 'int']],
          Close: ['int', []],
        });
        console.log('Define Modbus Success.');
      } catch (e) {
        console.log(`defineModbus error: ${e}`);
      }
      return dllPath;
    }
    
    export const CheckConnected = async (ip: string, port: number) => {
      try {
        const result = await checkPort(ip, port);
        return true;
      } catch (e) {
        return false;
      }
    }
    

    最后

    这里只写了 读写控制寄存器、读输入寄存器,支持数据类型 int16/int32/int64/float/double
    可以根据需要加入自己的代码,比如线圈的读写控制设备启停等

    动态链接库、源码地址

    链接: https://pan.baidu.com/s/1q40gHnftqzGJtUgfws_sdw 提取码: 72o2 
    
  • 相关阅读:
    【学习笔记】《Python深度学习》第三章:神经网络入门
    ​Kali-linux攻击路由器​
    分组聚合不再难:Pandas groupby使用指南
    第2-4-4章 规则引擎Drools规则属性-业务规则管理系统-组件化-中台
    1、Spring Security概述、快速入门
    【Vue Element-ui el-table组件 实现跨分页全选 可全选中当前页 也可选中全量数据】
    java基于springboot+vue的网上购物商城系统
    Android中单例模式正确实现方式
    寻找 llvm v3.5 的目标代码生成模块
    golang工程——grpc服务健康检查
  • 原文地址:https://www.cnblogs.com/miaokela/p/17735085.html