Closed fresanov closed 11 months ago
@fresanov if it's still relevant, here are some questions to clarify the issue:
@fresanov reviewing some code I found why you saw one byte instead of 3.
Let's review the process step by step.
field.Numeric
request.Field(3, "000000")
by using string representation of the numberrequest.Field
method does:func (m *Message) Field(id int, val string) error {
if f, ok := m.fields[id]; ok { // <--- we find the field
//...
return f.SetBytes([]byte(val)) // <--- we call `SetBytes` method on the field
}
// ...
}
SetBytes
of the Numeric
field. In our case it will be the else
branch:func (f *Numeric) SetBytes(b []byte) error {
if len(b) == 0 {
// ...
} else {
// otherwise parse the raw to an int
val, err := strconv.Atoi(string(b)) // <--- if we convert "000000" into `int` it will be 0
if err != nil {
return utils.NewSafeError(err, "failed to convert into number")
}
f.value = val
}
// ...
}
as you can see we convert processing code "000000" into int
and the underlying value of the field is 0
.
When you pack it, it results in a single byte (0x00). That explains why you see a single byte and not three bytes. Receiving side will fail to unpack it as it expects to get 3 bytes, not 1.
You may ask why it does not fail as the length of the packed data does not match the specified length 6
. As far as I remember, there were some issues with both BCD encoding and returning errors when the packed length didn't match the specified one.
Keep it as Numeric
but set padding for the field like this:
3: field.NewNumeric(&field.Spec{
Length: 6,
Description: "Processing Code",
Enc: encoding.BCD,
Pref: prefix.BCD.Fixed,
Pad: padding.Left('0'),
}),
when the field is packed, the value will be padded with 0
on the left side. When the data for the field is unpacked, 0
will be removed from the left side, resulting in a single 0 (int)
.
Use field.String
instead of field.Numeric
and then your 000000
will stay as is during packing and unpacking.
I apologize for the delayed resolution of the issue.
@alovak Thank you for the response. I determined that the root cause of this behavior is this method in numeric.go file in "field" package:
func (f *Numeric) SetBytes(b []byte) error {
if len(b) == 0 {
// for a length 0 raw, string(raw) would become "" which makes Atoi return an error
// however for example "0000" (value 0 left-padded with '0') should have 0 as output, not an error
// so if the length of raw is 0, set f.value to 0 instead of parsing the raw
f.value = 0
} else {
// otherwise parse the raw to an int
val, err := strconv.Atoi(string(b))
if err != nil {
return utils.NewSafeError(err, "failed to convert into number")
}
f.value = val
}
if f.data != nil {
*(f.data) = *f
}
return nil
}
On line 50 ( val, err := strconv.Atoi(string(b)) ) casting a byte slice of zeros of any length to string will result in single '0' character string and subsequently when converting it to int this will result in a single 0 digit.
I will try your workaround. Thank you.
@fresanov please, feel free to close the issue if the solution worked for you
I am trying to construct a message which has processing code of all zeros. This is a common situation, combination of MTI 0100 and processing code 000000 represents sale. However I get an error when trying to decode such a message: failed to unpack field 3 (Processing Code): failed to decode content: not enough data to decode. expected len 3, got 1
Here is my code for setting fields:
My spec for these fields:
I captured the message with wireshark, I think the issue is that the processing code in this case in encoded as one 0x00 byte. Instead it should be encoded as three zero bytes: 0x00 0x00 0x00