Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support modbus ascii mode #94

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 74 additions & 8 deletions internal/driver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,29 @@ type ConnectionInfo struct {

func createConnectionInfo(protocols map[string]models.ProtocolProperties) (info *ConnectionInfo, err error) {
protocolRTU, rtuExist := protocols[ProtocolRTU]
protocolASCII, asciiExist := protocols[ProtocolASCII]
protocolTCP, tcpExist := protocols[ProtocolTCP]

if rtuExist && tcpExist {
return info, fmt.Errorf("unsupported multiple protocols, please choose %s or %s, not both", ProtocolRTU, ProtocolTCP)
} else if !rtuExist && !tcpExist {
return info, fmt.Errorf("unable to create connection info, protocol config '%s' or %s not exist", ProtocolRTU, ProtocolTCP)
}

if rtuExist {
if rtuExist && !asciiExist && !tcpExist {
fmt.Printf("configs modbus rtu mode, now create connection")
info, err = createRTUConnectionInfo(protocolRTU)
if err != nil {
return nil, err
}
} else if tcpExist {
} else if !rtuExist && asciiExist && !tcpExist {
fmt.Printf("configs modbus ascii mode, now create connection")
info, err = createASCIIConnectionInfo(protocolASCII)
if err != nil {
return nil, err
}
} else if !rtuExist && !asciiExist && tcpExist {
fmt.Printf("configs modbus tcp mode, now create connection")
info, err = createTcpConnectionInfo(protocolTCP)
if err != nil {
return nil, err
}
} else {
return info, fmt.Errorf("unsupported modbus protocol, please config one of %s, %s or %s mode", ProtocolRTU, ProtocolASCII, ProtocolTCP)
}

return info, nil
Expand Down Expand Up @@ -112,6 +117,67 @@ func createRTUConnectionInfo(rtuProtocol map[string]string) (info *ConnectionInf
}, nil
}

func createASCIIConnectionInfo(asciiProtocol map[string]string) (info *ConnectionInfo, err error) {
errorMessage := "unable to create ASCII connection info, protocol config '%s' not exist"
address, ok := asciiProtocol[Address]
if !ok {
return nil, fmt.Errorf(errorMessage, Address)
}

us, ok := asciiProtocol[UnitID]
if !ok {
return nil, fmt.Errorf(errorMessage, UnitID)
}
unitID, err := strconv.ParseUint(us, 0, 8)
if err != nil {
return nil, fmt.Errorf("unitID value out of range(0-255). Error: %v", err)
}

br, ok := asciiProtocol[BaudRate]
if !ok {
return nil, fmt.Errorf(errorMessage, BaudRate)
}
baudRate, err := strconv.Atoi(br)
if err != nil {
return nil, err
}
ds, ok := asciiProtocol[DataBits]
if !ok {
return nil, fmt.Errorf(errorMessage, DataBits)
}
dataBits, err := strconv.Atoi(ds)
if err != nil {
return nil, err
}

ss, ok := asciiProtocol[StopBits]
if !ok {
return nil, fmt.Errorf(errorMessage, StopBits)
}
stopBits, err := strconv.Atoi(ss)
if err != nil {
return nil, err
}

parity, ok := asciiProtocol[Parity]
if !ok {
return nil, fmt.Errorf(errorMessage, Parity)
}
if parity != "N" && parity != "O" && parity != "E" {
return nil, fmt.Errorf("invalid parity value, it should be N(None) or O(Odd) or E(Even)")
}

return &ConnectionInfo{
Protocol: ProtocolASCII,
Address: address,
BaudRate: baudRate,
DataBits: dataBits,
StopBits: stopBits,
Parity: parity,
UnitID: byte(unitID),
}, nil
}

func createTcpConnectionInfo(tcpProtocol map[string]string) (info *ConnectionInfo, err error) {
errorMessage := "unable to create TCP connection info, protocol config '%s' not exist"
address, ok := tcpProtocol[Address]
Expand Down
30 changes: 30 additions & 0 deletions internal/driver/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,36 @@ import (
"github.com/edgexfoundry/go-mod-core-contracts/models"
)

func TestCreateASCIIConnectionInfo_dataBits7(t *testing.T) {
address := "/dev/USB0tty"
baudRate := 19200
dataBits := 7
stopBits := 1
parity := "N"
unitID := uint8(255)
protocols := map[string]models.ProtocolProperties{
ProtocolASCII: {
Address: address,
UnitID: "255",
BaudRate: "19200",
DataBits: "7",
StopBits: "1",
Parity: "N",
},
}

connectionInfo, err := createConnectionInfo(protocols)

if err != nil {
t.Fatalf("Fail to create connectionInfo. Error: %v", err)
}
if connectionInfo.Protocol != ProtocolASCII || connectionInfo.Address != address || connectionInfo.UnitID != unitID ||
connectionInfo.BaudRate != baudRate || connectionInfo.DataBits != dataBits || connectionInfo.StopBits != stopBits ||
connectionInfo.Parity != parity {
t.Fatalf("Unexpect test result. %v should match to %v ", connectionInfo, protocols)
}
}

func TestCreateRTUConnectionInfo_unitID255(t *testing.T) {
address := "/dev/USB0tty"
baudRate := 19200
Expand Down
51 changes: 34 additions & 17 deletions internal/driver/modbusclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,57 @@
package driver

import (
"encoding/binary"
"fmt"
"log"
"os"
"strings"
"encoding/binary"

MODBUS "github.com/goburrow/modbus"
)

// ModbusClient is used for connecting the device and read/write value
type ModbusClient struct {
// IsModbusTcp is a value indicating the connection type
IsModbusTcp bool
// TCPClientHandler is ued for holding device TCP connection
// ModbusProtocol is a value indicating the connection type
ModbusProtocol string
// TCPClientHandler is used for holding device TCP connection
TCPClientHandler MODBUS.TCPClientHandler
// TCPClientHandler is ued for holding device RTU connection
// RTUClientHandler is used for holding device RTU connection
RTUClientHandler MODBUS.RTUClientHandler
// ASCIIClientHandler is used for holding device ASCII connection
ASCIIClientHandler MODBUS.ASCIIClientHandler

client MODBUS.Client
}

func (c *ModbusClient) OpenConnection() error {
var err error
var newClient MODBUS.Client
if c.IsModbusTcp {
if c.ModbusProtocol == ProtocolTCP {
err = c.TCPClientHandler.Connect()
newClient = MODBUS.NewClient(&c.TCPClientHandler)
driver.Logger.Info(fmt.Sprintf("Modbus client create TCP connection."))
} else {
} else if c.ModbusProtocol == ProtocolRTU {
err = c.RTUClientHandler.Connect()
newClient = MODBUS.NewClient(&c.RTUClientHandler)
driver.Logger.Info(fmt.Sprintf("Modbus client create RTU connection."))
} else {
err = c.ASCIIClientHandler.Connect()
newClient = MODBUS.NewClient(&c.ASCIIClientHandler)
driver.Logger.Info(fmt.Sprintf("Modbus client create ASCII connection."))
}
c.client = newClient
return err
}

func (c *ModbusClient) CloseConnection() error {
var err error
if c.IsModbusTcp {
if c.ModbusProtocol == ProtocolTCP {
err = c.TCPClientHandler.Close()

} else {
} else if c.ModbusProtocol == ProtocolRTU {
err = c.RTUClientHandler.Close()
} else {
err = c.ASCIIClientHandler.Close()
}
return err
}
Expand Down Expand Up @@ -104,9 +111,9 @@ func (c *ModbusClient) SetValue(commandInfo interface{}, value []byte) error {

case HOLDING_REGISTERS:
if modbusCommandInfo.Length == 1 {
result, err = c.client.WriteSingleRegister(uint16(modbusCommandInfo.StartingAddress), binary.BigEndian.Uint16(value))
} else {
result, err = c.client.WriteMultipleRegisters(uint16(modbusCommandInfo.StartingAddress), modbusCommandInfo.Length, value)
result, err = c.client.WriteSingleRegister(uint16(modbusCommandInfo.StartingAddress), binary.BigEndian.Uint16(value))
} else {
result, err = c.client.WriteMultipleRegisters(uint16(modbusCommandInfo.StartingAddress), modbusCommandInfo.Length, value)
}
default:
}
Expand All @@ -124,15 +131,14 @@ func NewDeviceClient(connectionInfo *ConnectionInfo) (*ModbusClient, error) {
var err error
var tcpClientHandler = new(MODBUS.TCPClientHandler)
var rtuClientHandler = new(MODBUS.RTUClientHandler)
var asciiClientHandler = new(MODBUS.ASCIIClientHandler)

if connectionInfo.Protocol == ProtocolTCP {
client.IsModbusTcp = true
}
if client.IsModbusTcp {
tcpClientHandler = MODBUS.NewTCPClientHandler(fmt.Sprintf("%s:%d", connectionInfo.Address, connectionInfo.Port))
tcpClientHandler.SlaveId = byte(connectionInfo.UnitID)
tcpClientHandler.IdleTimeout = 0
tcpClientHandler.Logger = log.New(os.Stdout, "", log.LstdFlags)
} else {
} else if connectionInfo.Protocol == ProtocolRTU {
serialParams := strings.Split(connectionInfo.Address, ",")
rtuClientHandler = MODBUS.NewRTUClientHandler(serialParams[0])
rtuClientHandler.SlaveId = byte(connectionInfo.UnitID)
Expand All @@ -142,9 +148,20 @@ func NewDeviceClient(connectionInfo *ConnectionInfo) (*ModbusClient, error) {
rtuClientHandler.StopBits = connectionInfo.StopBits
rtuClientHandler.Parity = connectionInfo.Parity
rtuClientHandler.Logger = log.New(os.Stdout, "", log.LstdFlags)
} else {
serialParams := strings.Split(connectionInfo.Address, ",")
asciiClientHandler = MODBUS.NewASCIIClientHandler(serialParams[0])
asciiClientHandler.SlaveId = byte(connectionInfo.UnitID)
asciiClientHandler.IdleTimeout = 0
asciiClientHandler.BaudRate = connectionInfo.BaudRate
asciiClientHandler.DataBits = connectionInfo.DataBits
asciiClientHandler.StopBits = connectionInfo.StopBits
asciiClientHandler.Parity = connectionInfo.Parity
asciiClientHandler.Logger = log.New(os.Stdout, "", log.LstdFlags)
}

client.TCPClientHandler = *tcpClientHandler
client.RTUClientHandler = *rtuClientHandler
client.ASCIIClientHandler = *asciiClientHandler
return client, err
}
5 changes: 3 additions & 2 deletions internal/driver/protocolpropertykey.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
package driver

const (
ProtocolTCP = "modbus-tcp"
ProtocolRTU = "modbus-rtu"
ProtocolTCP = "modbus-tcp"
ProtocolRTU = "modbus-rtu"
ProtocolASCII = "modbus-ascii"

Address = "Address"
Port = "Port"
Expand Down