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

modbus,动态链接库,语言,使用,go · 浏览次数 : 42

小编点评

**定义函数** ``` DefineModbus = async () => {...}; ``` **读写控制寄存器** ``` ReadByModbusTcp = async ( slaveId: number, startAddress: number, dataType: string, method: string = 'Input' ) => {...}; ``` **写入控制寄存器** ``` WriteByModbusTcp = async ( slaveId: number, startAddress: number, dataType: string, val: number,) => {...}; ``` **数据类型** ``` doublePtr ``` **连接** ``` connectModbus = (ip: string, port: number) => {...}; ``` **关闭** ``` closeModbus = async () => {...}; ``` **读取输入寄存器** ``` ReadInputRegister = async ( slaveId: number, startAddress: number, dataType: string, method: string = 'Input' ) => {...}; ``` **写入输出寄存器** ``` WriteOutputRegister = async ( slaveId: number, startAddress: number, dataType: string, val: number,) => {...}; ``` **数据** ``` double ``` **数据类型** ``` int16, int32, int64, float, double ``` **数据** ``` double ``` **数据** ``` double ``` **数据** ``` double ```

正文

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 <stdlib.h>
*/
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 <stdlib.h>
*/
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 

与Modbus动态链接库供多语言使用 | Go相似的内容:

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

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

一款基于C#开发的通讯调试工具(支持Modbus RTU、MQTT调试)

前言 今天大姚给大家分享一款基于C#、WPF、Prism、MaterialDesign、HandyControl开发的通讯调试工具(支持Modbus RTU、MQTT调试,界面色彩丰富):Wu.CommTool。 工具特点 工具界面色彩丰富。 支持Modbus RTU、MQTT服务器、MQTT客户端

基于WebSocket的modbus通信(三)- websocket和串口

WebSocket传递ModbusTCP数据包 错误纠正 上一篇还有个错误,就是客户端写数据时服务端不需要响应,但我的服务端响应了的。我选择改客户端,把写数据时接收到的响应丢弃。 PrintBytes(ADUMessage.Serialze(request), "请求"); if (Client !

基于WebSocket的modbus通信(二)- 客户端

上一篇已经实现了ModbusTcp服务器和8个主要的功能码,只是还没有实现错误处理功能。 但是在测试客户端时却发现了上一篇的一个错误,那就是写数据成功,服务器不需要响应。 接下来要做的就是实现ModbusTcp客户端。有了清晰的协议,代码循规蹈矩的写就行了。 效果 原始数据 其中只读寄存器和线圈都有

基于WebSocket的modbus通信(一)- 服务器

ModbusTcp协议是基于tcp的,但不是说一定要通过tcp协议才能传输,只要能传输二进制的地方都可以。比如WebSocket协议。 但由于目前我只有tcp上面的modbus服务器实现,所以我必须先用tcp连接借助已有工具来验证我的服务器是否写正确。 效果 ModBusTCP协议报文 ModBus

上位机开发福利!快速掌握.NET中的Modbus通信

安装nuget包 Wesky.Net.OpenTools 1.0.8或以上版本。支持.net framework 4.6以上版本,以及所有.net core以及以上版本引用。 开发一个简单的Winform界面,用来测试使用。如需该winform的demo,可以在公众号【Dotnet Dancer】后

NET工控,上位机,Modbus485网口/串口通讯(鸣志步进电机,鸣志伺服电机,松下伺服电机,华庆军继电器模块)

先上两个通用Modbus帮助类,下面这个是多线程不安全版,在多线程多电机同一端口通信下,可能造成步进电机丢步或者输出口无响应等,还有个多线程安全版,只是基于这个不安全版加上了LOCK,THIS using Modbus.Device; using Sunny.UI; using System; us

光伏储能电厂设备连接iec61850平台解决方案

在当今日益发展的电力系统中,光伏储能技术以其独特的优势逐渐崭露头角,成为可再生能源领域的重要组成部分。而在光伏储能系统的运行与监控中,通信协议的选择与实现则显得至关重要。本文将重点介绍光伏储能系统中的Modbus协议、电力IEC 61850平台,以及如何通过协议转换网关实现Modbus转IEC 61

EthernetIP IO从站设备数据 转opc ua项目案例

1 案例说明 设置网关采集EthernetIP IO设备数据 把采集的数据转成opc ua协议转发给其他系统。 2 VFBOX网关工作原理 VFBOX网关是协议转换网关,是把一种协议转换成另外一种协议。网关可以采集西门子,欧姆龙,三菱,AB PLC,DLT645,DLT698电表,modbus rt