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

feat: supply string type #262

Merged
merged 9 commits into from
Aug 3, 2021
2 changes: 1 addition & 1 deletion cmd/res/configuration.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,4 @@ UpdateLastConnected = false
Labels = []
EnableAsyncReadings = true
AsyncBufferSize = 16
UseMessageBus = false
UseMessageBus = true
23 changes: 21 additions & 2 deletions cmd/res/devices/modbus.test.devices.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Pre-define Devices
[[DeviceList]]
Name = 'Modbus-TCP-test-device'
ProfileName = 'Test.Device.Modbus.Profile'
ProfileName = 'Test-Device-Modbus-Profile'
Description = 'This device is a product for monitoring and controlling digital inputs and outputs over a LAN.'
labels = [ 'Air conditioner','modbus TCP' ]
[DeviceList.Protocols]
Expand All @@ -18,7 +18,7 @@

[[DeviceList]]
Name = 'Modbus-RTU-test-device'
ProfileName = 'Test.Device.Modbus.Profile'
ProfileName = 'Test-Device-Modbus-Profile'
Description = 'This device is a product for monitoring and controlling digital inputs and outputs over a LAN.'
labels = [ 'Air conditioner','modbus RTU' ]
[DeviceList.Protocols]
Expand All @@ -31,3 +31,22 @@
UnitID = '1'
Timeout = "5"
IdleTimeout = "5"

# Pre-define Devices
[[DeviceList]]
Name = 'Modbus-TCP-Read-String'
ProfileName = 'Test.Device.Modbus.String.Profile'
hongda3141 marked this conversation as resolved.
Show resolved Hide resolved
Description = 'use for auto read a string value'
labels = [ 'modbus TCP' ]
[DeviceList.Protocols]
[DeviceList.Protocols.modbus-tcp]
Address = '0.0.0.0'
Port = '1502'
UnitID = '1'
Timeout = '5'
IdleTimeout = '5'
[[DeviceList.AutoEvents]]
Interval = '20s'
OnChange = false
SourceName = 'ReadString'

2 changes: 1 addition & 1 deletion cmd/res/profiles/modbus.test.device.profile.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Test.Device.Modbus.Profile"
name: "Test-Device-Modbus-Profile"
manufacturer: "Cool Automation"
model: "CoolMasterNet"
labels:
Expand Down
41 changes: 41 additions & 0 deletions cmd/res/profiles/modbus.test.device.string.profile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: "Test-Device-Modbus-String-Profile"
manufacturer: "no manufacturer"
model: "string value type"
labels:
- "no lable"
description: "this profile is testing string value type with read and write"

deviceResources:
-
name: "StringA"
description: "no description"
attributes:
{ primaryTable: "INPUT_REGISTERS", startingAddress: 1, stringRegisterSize: 1}
properties:
valueType: "String"
readWrite: "R"

-
name: "StringB"
description: "no description"
attributes:
{ primaryTable: "HOLDING_REGISTERS", startingAddress: 15, stringRegisterSize: 5}
properties:
valueType: "String"
readWrite: "RW"



deviceCommands:
-
name: "ReadString"
readWrite: "R"
isHidden: false
resourceOperations:
- { deviceResource: "StringA"}
-
name: "WriteString"
readWrite: "RW"
isHidden: false
resourceOperations:
- { deviceResource: "StringB" }
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module github.com/edgexfoundry/device-modbus-go

require (
github.com/edgexfoundry/device-sdk-go/v2 v2.0.0-dev.77
github.com/edgexfoundry/go-mod-core-contracts/v2 v2.0.0-dev.100
github.com/edgexfoundry/device-sdk-go/v2 v2.0.0-dev.86
github.com/edgexfoundry/go-mod-core-contracts/v2 v2.0.0-dev.103
github.com/goburrow/modbus v0.1.0
github.com/goburrow/serial v0.1.0 // indirect
github.com/hashicorp/go-sockaddr v1.0.1 // indirect
Expand Down
5 changes: 4 additions & 1 deletion internal/driver/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const (
// RAW_TYPE define binary data type which read from Modbus device
RAW_TYPE = "rawType"

// STRING_REGISTER_SIZE E.g. "abcd" need 4 bytes as is 2 registers(2 words), so STRING_REGISTER_SIZE=2
STRING_REGISTER_SIZE = "stringRegisterSize"
SERVICE_STOP_WAIT_TIME = 1
)

Expand All @@ -58,5 +60,6 @@ var ValueTypeBitCountMap = map[string]uint16{
common.ValueTypeFloat32: 32,
common.ValueTypeFloat64: 64,

common.ValueTypeBool: 1,
common.ValueTypeBool: 1,
common.ValueTypeString: 16,
}
48 changes: 38 additions & 10 deletions internal/driver/deviceclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,17 @@ func createCommandInfo(req *models.CommandRequest) (*CommandInfo, error) {
return nil, err
}
}
length := calculateAddressLength(primaryTable, rawType)
var length uint16
if req.Type == common.ValueTypeString {
length, err = castStartingAddress(req.Attributes[STRING_REGISTER_SIZE])
if err != nil {
return nil, err
} else if (length > 123) || (length < 1) {
return nil, errors.NewCommonEdgeX(errors.KindLimitExceeded, fmt.Sprintf("register size should be within the range of 1~123, get %v.", length), nil)
}
} else {
length = calculateAddressLength(primaryTable, rawType)
}

var isByteSwap = false
if _, ok := req.Attributes[IS_BYTE_SWAP]; ok {
Expand Down Expand Up @@ -157,6 +167,8 @@ func TransformDataBytesToResult(req *models.CommandRequest, dataBytes []byte, co
if (dataBytes[0] & 1) > 0 {
res = true
}
case common.ValueTypeString:
res = trimString(dataBytes)
weichou1229 marked this conversation as resolved.
Show resolved Hide resolved
default:
return nil, fmt.Errorf("return result fail, none supported value type: %v", commandInfo.ValueType)
}
Expand All @@ -175,15 +187,18 @@ func TransformDataBytesToResult(req *models.CommandRequest, dataBytes []byte, co
func TransformCommandValueToDataBytes(commandInfo *CommandInfo, value *models.CommandValue) ([]byte, error) {
var err error
var byteCount = calculateByteCount(commandInfo)
var dataBytes []byte
buf := new(bytes.Buffer)
err = binary.Write(buf, binary.BigEndian, value.Value)
if err != nil {
return nil, fmt.Errorf("failed to transform %v to []byte", value.Value)
}
if commandInfo.ValueType != common.ValueTypeString {
err = binary.Write(buf, binary.BigEndian, value.Value)
if err != nil {
return nil, fmt.Errorf("failed to transform %v to []byte", value.Value)
}

numericValue := buf.Bytes()
var maxSize = uint16(len(numericValue))
var dataBytes = numericValue[maxSize-byteCount : maxSize]
numericValue := buf.Bytes()
var maxSize = uint16(len(numericValue))
dataBytes = numericValue[maxSize-byteCount : maxSize]
}

_, ok := ValueTypeBitCountMap[commandInfo.ValueType]
if !ok {
Expand All @@ -195,7 +210,7 @@ func TransformCommandValueToDataBytes(commandInfo *CommandInfo, value *models.Co
dataBytes = swap32BitDataBytes(dataBytes, commandInfo.IsByteSwap, commandInfo.IsWordSwap)
}

// Cast value according to the rawType, this feature only converts float value to integer 32bit value
// Cast value according to the rawType, this feature converts float value to integer 32bit value
if commandInfo.ValueType == common.ValueTypeFloat32 {
val, edgexErr := value.Float32Value()
if edgexErr != nil {
Expand Down Expand Up @@ -228,8 +243,21 @@ func TransformCommandValueToDataBytes(commandInfo *CommandInfo, value *models.Co
return dataBytes, err
}
}
} else if commandInfo.ValueType == common.ValueTypeString {
// Cast value of string type
oriStr := value.ValueToString()
tempBytes := []byte(oriStr)
bytesL := len(tempBytes)
oriByteL := int(commandInfo.Length * 2)
if bytesL < oriByteL {
less := make([]byte, oriByteL-bytesL)
dataBytes = append(tempBytes, less...)
} else if bytesL > oriByteL {
dataBytes = tempBytes[:oriByteL]
} else {
dataBytes = []byte(oriStr)
}
}

driver.Logger.Debugf("Transfer CommandValue to dataBytes for write command, %v, %v", commandInfo.ValueType, dataBytes)
return dataBytes, err
}
Expand Down
21 changes: 21 additions & 0 deletions internal/driver/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,24 @@ func castSwapAttribute(i interface{}) (res bool, err errors.EdgeX) {
}
return res, nil
}

func trimString(input []byte) string {
head := 0
for idx, _ := range input {
if input[idx] == 0 {
head = idx + 1
} else {
break
}
}
tempList := input[head:]
end := len(tempList)
for idx, _ := range tempList {
if tempList[len(tempList)-1-idx] == 0 {
end = len(tempList) - 1 - idx
} else {
break
}
}
return string(tempList[:end])
}