lxp-packet 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +8 -0
- data/README.md +18 -2
- data/doc/LXP_REGISTERS.txt +4 -3
- data/lib/lxp/packet/base.rb +16 -11
- data/lib/lxp/packet/heartbeat.rb +0 -4
- data/lib/lxp/packet/read_hold.rb +26 -8
- data/lib/lxp/packet/write_single.rb +1 -5
- data/lib/lxp/version.rb +1 -1
- data/lxp-packet.gemspec +1 -0
- data/spec/parser_spec.rb +80 -0
- data/spec/read_hold_spec.rb +34 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/write_single_spec.rb +33 -0
- metadata +22 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f690a3ff0e519ed9f0c68152117bfcd4ece48d2ffd67c32b83cd1b2bafc356b5
|
|
4
|
+
data.tar.gz: 4f35b9e46c92659e7bd5c366e3cc6bcbe9c4646e17278b6a8df3f7c3edb220a5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d19df5ab0f0cbe0568c4f7277a1ea84c071f30a3c882cabc16464b0a6a1b2850d27deeb191e74920d5f6da7fb052fde8b89768a535117a369c1724b75cfc4ef4
|
|
7
|
+
data.tar.gz: 418b00400f1c147d45dccb5f0d74660baccd9a5a5d88fb81cd0e25c86115550f1609f1482d9ea8fca9f4f9ec56d86b6bd5654138d7d2561d8a9595dc0fb4f466
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2020-04-07
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- parsing of ReadHold packets with multiple registers/values
|
|
15
|
+
- beginnings of a test suite!
|
|
16
|
+
|
|
17
|
+
|
|
10
18
|
## [0.3.0] - 2020-04-02
|
|
11
19
|
|
|
12
20
|
### Fixed
|
data/README.md
CHANGED
|
@@ -96,6 +96,22 @@ r = read_reply(sock, pkt)
|
|
|
96
96
|
puts "Received: #{r.value}" # should be discharge cut-off value
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
+
Usually, `ReadHold` instances contain the details of just one register. However, it is possible they can contain multiple. Pressing "Read" on the LuxPower Web Portal provokes the inverter into sending out 5 packets that each contain multiple registers, for example. [TODO: work out how to request these packets ourselves]
|
|
100
|
+
|
|
101
|
+
To access these, you can use subscript notation to get a register directly, or call `#to_h` to get a hash of registers/values. For convenience this also works with single register packets, though obviously only one subscript will ever return data, and `to_h` will only have one key.
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
# assuming pkt is a parsed packet with multiple registers/values:
|
|
105
|
+
pkt[0] # => 35462 # value of register 0
|
|
106
|
+
pkt.to_h # { 0 => 35462, 1 => 1, ... }
|
|
107
|
+
|
|
108
|
+
# assuming pkt is a parsed packet with only register 21:
|
|
109
|
+
pkt[21] # => value of register 21
|
|
110
|
+
pkt[22] # => nil
|
|
111
|
+
pkt.to_h # { 21 => 62292 }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
|
|
99
115
|
### Writing
|
|
100
116
|
|
|
101
117
|
Updating a value on the inverter.
|
|
@@ -119,7 +135,7 @@ r = read_reply(sock, pkt)
|
|
|
119
135
|
puts "Received: #{r.value}" # should be new discharge cut-off value, 20
|
|
120
136
|
```
|
|
121
137
|
|
|
122
|
-
### Updating a
|
|
138
|
+
### Updating a bitwise register
|
|
123
139
|
|
|
124
140
|
The Lux has two registers that contain multiple settings. In `doc/LXP_REGISTERS.txt` you can see them at 21 and 110. They have two bytes.
|
|
125
141
|
|
|
@@ -127,7 +143,7 @@ This library combines them into a 16bit word, so that the constants in `LXP::Pac
|
|
|
127
143
|
|
|
128
144
|
First you need to read the previous value, update it with a new bit, then write it back. This is really just a combination of the two above examples.
|
|
129
145
|
|
|
130
|
-
This enables AC charge. You need to OR the bit with the previous value so as not to change other settings
|
|
146
|
+
This example enables AC charge. You need to OR the bit with the previous value so as not to change other settings stored in register 21.
|
|
131
147
|
|
|
132
148
|
It could be improved not to bother doing the write if it was already enabled.
|
|
133
149
|
|
data/doc/LXP_REGISTERS.txt
CHANGED
|
@@ -9,9 +9,10 @@ Registers 21 and 110 are bitmasks; normally you fetch the previous
|
|
|
9
9
|
settings, apply your changes, and set the new value back.
|
|
10
10
|
|
|
11
11
|
0 MODEL
|
|
12
|
-
2 SERIAL_NUM
|
|
13
|
-
7 FW_CODE
|
|
14
|
-
|
|
12
|
+
2-8 SERIAL_NUM
|
|
13
|
+
7 FW_CODE ?
|
|
14
|
+
9-10 ?
|
|
15
|
+
12-14 TIME
|
|
15
16
|
15 COM_ADDR
|
|
16
17
|
16 LANGUAGE
|
|
17
18
|
20 PV_INPUT_MODE
|
data/lib/lxp/packet/base.rb
CHANGED
|
@@ -26,7 +26,6 @@ class LXP
|
|
|
26
26
|
|
|
27
27
|
self.protocol = 1
|
|
28
28
|
|
|
29
|
-
# length after first 6 bytes maybe?
|
|
30
29
|
self.packet_length = 32
|
|
31
30
|
|
|
32
31
|
@header[6] = 1 # unsure, always seems to be 1
|
|
@@ -89,6 +88,10 @@ class LXP
|
|
|
89
88
|
@header[7] = tcp_function & 0xff
|
|
90
89
|
end
|
|
91
90
|
|
|
91
|
+
def datalog_serial
|
|
92
|
+
@header[8, 10].pack('C*')
|
|
93
|
+
end
|
|
94
|
+
|
|
92
95
|
# Passed as a string
|
|
93
96
|
def datalog_serial=(datalog_serial)
|
|
94
97
|
@header[8, 10] = datalog_serial.bytes
|
|
@@ -111,6 +114,10 @@ class LXP
|
|
|
111
114
|
@data[1] = device_function
|
|
112
115
|
end
|
|
113
116
|
|
|
117
|
+
def inverter_serial
|
|
118
|
+
@data[2, 10].pack('C*')
|
|
119
|
+
end
|
|
120
|
+
|
|
114
121
|
# Passed as a string
|
|
115
122
|
def inverter_serial=(inverter_serial)
|
|
116
123
|
@data[2, 10] = inverter_serial.bytes
|
|
@@ -125,12 +132,6 @@ class LXP
|
|
|
125
132
|
@data[13] = (register >> 8) & 0xff
|
|
126
133
|
end
|
|
127
134
|
|
|
128
|
-
def value_length_byte?
|
|
129
|
-
@value_length_byte ||=
|
|
130
|
-
protocol == 2 &&
|
|
131
|
-
device_function != DeviceFunctions::WRITE_SINGLE
|
|
132
|
-
end
|
|
133
|
-
|
|
134
135
|
def value_length
|
|
135
136
|
if value_length_byte?
|
|
136
137
|
@data[14]
|
|
@@ -142,13 +143,11 @@ class LXP
|
|
|
142
143
|
# protocol 1 has value at 14 and 15
|
|
143
144
|
# protocol 2 has length at 14, then that many bytes of values
|
|
144
145
|
#
|
|
145
|
-
|
|
146
|
-
#
|
|
147
|
-
def value
|
|
146
|
+
def values
|
|
148
147
|
if value_length_byte?
|
|
149
148
|
@data[15, value_length]
|
|
150
149
|
else
|
|
151
|
-
@data[14] | @data[15] << 8
|
|
150
|
+
@data[14, 2] # | @data[15] << 8
|
|
152
151
|
end
|
|
153
152
|
end
|
|
154
153
|
|
|
@@ -171,6 +170,12 @@ class LXP
|
|
|
171
170
|
|
|
172
171
|
private
|
|
173
172
|
|
|
173
|
+
def value_length_byte?
|
|
174
|
+
@value_length_byte ||=
|
|
175
|
+
protocol == 2 &&
|
|
176
|
+
device_function != DeviceFunctions::WRITE_SINGLE
|
|
177
|
+
end
|
|
178
|
+
|
|
174
179
|
def crc16_modbus(arr)
|
|
175
180
|
arr.length.times.inject(0xffff) do |r, n|
|
|
176
181
|
r ^= arr[n]
|
data/lib/lxp/packet/heartbeat.rb
CHANGED
|
@@ -15,10 +15,6 @@ class LXP
|
|
|
15
15
|
#
|
|
16
16
|
# They have no data and no checksum, so there's really not a lot here.
|
|
17
17
|
#
|
|
18
|
-
# Ideally these could be instantiated via .parse, but they only seem to
|
|
19
|
-
# have one byte for the length, and .parse expects two. There's nothing
|
|
20
|
-
# to parse anyway so never mind.
|
|
21
|
-
#
|
|
22
18
|
class Heartbeat < Base
|
|
23
19
|
def initialize
|
|
24
20
|
super
|
data/lib/lxp/packet/read_hold.rb
CHANGED
|
@@ -18,18 +18,36 @@ class LXP
|
|
|
18
18
|
self.value = 1
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
#
|
|
21
|
+
# Return the first value in a ReadHold packet. This is normally used
|
|
22
|
+
# when the packet only has one value.
|
|
22
23
|
#
|
|
23
|
-
#
|
|
24
|
+
# #values returns an Array. This converts it to an int.
|
|
24
25
|
#
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
def value(offset = 0)
|
|
27
|
+
Utils.int(values[offset, 2])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Subscript notation is used when the ReadHold packet has multiple
|
|
31
|
+
# registers in it. This is indicated by value_length > 2.
|
|
27
32
|
#
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
# In this case, #register tells us the first register in the values, then
|
|
34
|
+
# each 2 bytes are subsequent registers.
|
|
35
|
+
#
|
|
36
|
+
def [](reg_num)
|
|
37
|
+
offset = (reg_num - register) * 2
|
|
38
|
+
|
|
39
|
+
return if offset.negative? || offset > data_length
|
|
40
|
+
|
|
41
|
+
value(offset)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Return a Hash of all register->values in the ReadHold packet.
|
|
45
|
+
def to_h
|
|
46
|
+
r = register
|
|
30
47
|
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
values.each_slice(2).each_with_index.map do |v, idx|
|
|
49
|
+
[r + idx, Utils.int(v)]
|
|
50
|
+
end.to_h
|
|
33
51
|
end
|
|
34
52
|
end
|
|
35
53
|
end
|
|
@@ -27,14 +27,10 @@ class LXP
|
|
|
27
27
|
#
|
|
28
28
|
# Raise if not, as that is not expected?
|
|
29
29
|
#
|
|
30
|
-
# Base#value will return an int for protocol 1, or an Array
|
|
31
|
-
# for protocol 2. If we can, convert that Array to an int.
|
|
32
|
-
#
|
|
33
30
|
def value
|
|
34
31
|
raise 'value_length not 2?' unless value_length == 2
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
r.is_a?(Array) ? Utils.int(r, 2) : r
|
|
33
|
+
Utils.int(values[0, 2])
|
|
38
34
|
end
|
|
39
35
|
end
|
|
40
36
|
end
|
data/lib/lxp/version.rb
CHANGED
data/lxp-packet.gemspec
CHANGED
data/spec/parser_spec.rb
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe LXP::Packet::Parser do
|
|
4
|
+
let(:parser) { LXP::Packet::Parser.new(input.pack('C*')) }
|
|
5
|
+
let(:packet) { parser.parse }
|
|
6
|
+
|
|
7
|
+
subject { packet }
|
|
8
|
+
|
|
9
|
+
context 'ReadHold multi data (registers 0-22)' do
|
|
10
|
+
let(:input) { [161, 26, 2, 0, 77, 0, 1, 194, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 63, 0, 1, 3, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 0, 0, 46, 134, 138, 1, 0, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 66, 65, 65, 65, 1, 10, 10, 1, 0, 0, 20, 4, 7, 16, 14, 49, 1, 0, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 84, 243, 176, 4, 40, 224] }
|
|
11
|
+
|
|
12
|
+
it { is_expected.to be_a LXP::Packet::ReadHold }
|
|
13
|
+
|
|
14
|
+
it 'has the correct attributes' do
|
|
15
|
+
expect(packet).to have_attributes register: 0,
|
|
16
|
+
values: [134, 138, 1, 0, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 66, 65, 65, 65, 1, 10, 10, 1, 0, 0, 20, 4, 7, 16, 14, 49, 1, 0, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 84, 243, 176, 4],
|
|
17
|
+
to_h: { 0 => 35_462, 1 => 1, 2 => 22_616, 3 => 22_616, 4 => 22_616, 5 => 22_616, 6 => 22_616, 7 => 16_706, 8 => 16_705, 9 => 2561, 10 => 266, 11 => 0, 12 => 1044, 13 => 4103, 14 => 12_558, 15 => 1, 16 => 1, 17 => 0, 18 => 0, 19 => 4, 20 => 0, 21 => 62_292, 22 => 1200 },
|
|
18
|
+
protocol: 2,
|
|
19
|
+
packet_length: 77, data_length: 63,
|
|
20
|
+
tcp_function: LXP::Packet::TcpFunctions::TRANSLATED_DATA,
|
|
21
|
+
device_function: LXP::Packet::DeviceFunctions::READ_HOLD,
|
|
22
|
+
inverter_serial: 'XXXXXXXXXX',
|
|
23
|
+
datalog_serial: 'cccccccccc',
|
|
24
|
+
bytes: input
|
|
25
|
+
expect(packet[22]).to eq 1200
|
|
26
|
+
|
|
27
|
+
# test out of bounds subscripts return nil
|
|
28
|
+
expect(packet[23]).to be_nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context 'ReadHold multi data (registers 80-113)' do
|
|
33
|
+
let(:input) { [161, 26, 2, 0, 99, 0, 1, 194, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 85, 0, 1, 3, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 80, 0, 68, 0, 0, 0, 0, 100, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, 50, 0, 111, 9, 252, 8, 20, 0, 5, 0, 0, 0, 0, 0, 0, 0, 48, 2, 144, 1, 66, 0, 66, 0, 0, 0, 0, 0, 10, 0, 56, 255, 38, 2, 0, 0, 144, 1, 2, 0, 0, 0, 0, 0, 0, 0, 193, 242] }
|
|
34
|
+
|
|
35
|
+
it { is_expected.to be_a LXP::Packet::ReadHold }
|
|
36
|
+
|
|
37
|
+
it 'has the correct attributes' do
|
|
38
|
+
expect(packet).to have_attributes register: 80,
|
|
39
|
+
values: [0, 0, 0, 0, 100, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, 50, 0, 111, 9, 252, 8, 20, 0, 5, 0, 0, 0, 0, 0, 0, 0, 48, 2, 144, 1, 66, 0, 66, 0, 0, 0, 0, 0, 10, 0, 56, 255, 38, 2, 0, 0, 144, 1, 2, 0, 0, 0, 0, 0, 0, 0],
|
|
40
|
+
to_h: { 80 => 0, 81 => 0, 82 => 100, 83 => 20, 84 => 0, 85 => 0, 86 => 0, 87 => 0, 88 => 0, 89 => 0, 90 => 230, 91 => 50, 92 => 2415, 93 => 2300, 94 => 20, 95 => 5, 96 => 0, 97 => 0, 98 => 0, 99 => 560, 100 => 400, 101 => 66, 102 => 66, 103 => 0, 104 => 0, 105 => 10, 106 => 65_336, 107 => 550, 108 => 0, 109 => 400, 110 => 2, 111 => 0, 112 => 0, 113 => 0 },
|
|
41
|
+
protocol: 2,
|
|
42
|
+
packet_length: 99, data_length: 85,
|
|
43
|
+
tcp_function: LXP::Packet::TcpFunctions::TRANSLATED_DATA,
|
|
44
|
+
device_function: LXP::Packet::DeviceFunctions::READ_HOLD,
|
|
45
|
+
inverter_serial: 'XXXXXXXXXX',
|
|
46
|
+
datalog_serial: 'cccccccccc',
|
|
47
|
+
bytes: input
|
|
48
|
+
expect(packet[80]).to eq 0
|
|
49
|
+
expect(packet[94]).to eq 20
|
|
50
|
+
expect(packet[113]).to eq 0
|
|
51
|
+
|
|
52
|
+
# test out of bounds subscripts return nil
|
|
53
|
+
expect(packet[114]).to be_nil
|
|
54
|
+
expect(packet[79]).to be_nil
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context 'ReadInput1 data' do
|
|
59
|
+
let(:input) { [161, 26, 2, 0, 111, 0, 1, 194, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 97, 0, 1, 4, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 0, 0, 80, 16, 0, 0, 0, 0, 0, 0, 0, 241, 1, 95, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 121, 2, 172, 9, 1, 0, 0, 0, 146, 19, 57, 2, 0, 0, 227, 0, 232, 3, 172, 9, 0, 11, 80, 112, 146, 19, 0, 0, 0, 0, 0, 0, 0, 0, 221, 0, 0, 0, 0, 0, 42, 0, 73, 0, 86, 0, 52, 0, 0, 0, 36, 0, 2, 0, 172, 14, 181, 11, 127, 131] }
|
|
60
|
+
|
|
61
|
+
it { is_expected.to be_a LXP::Packet::ReadInput1 }
|
|
62
|
+
|
|
63
|
+
it 'has the correct attributes' do
|
|
64
|
+
expect(packet).to have_attributes register: 0,
|
|
65
|
+
values: [16, 0, 0, 0, 0, 0, 0, 0, 241, 1, 95, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 121, 2, 172, 9, 1, 0, 0, 0, 146, 19, 57, 2, 0, 0, 227, 0, 232, 3, 172, 9, 0, 11, 80, 112, 146, 19, 0, 0, 0, 0, 0, 0, 0, 0, 221, 0, 0, 0, 0, 0, 42, 0, 73, 0, 86, 0, 52, 0, 0, 0, 36, 0, 2, 0, 172, 14, 181, 11],
|
|
66
|
+
protocol: 2,
|
|
67
|
+
packet_length: 111, data_length: 97,
|
|
68
|
+
tcp_function: LXP::Packet::TcpFunctions::TRANSLATED_DATA,
|
|
69
|
+
device_function: LXP::Packet::DeviceFunctions::READ_INPUT,
|
|
70
|
+
inverter_serial: 'XXXXXXXXXX',
|
|
71
|
+
datalog_serial: 'cccccccccc',
|
|
72
|
+
bytes: input
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
describe '#to_h' do
|
|
76
|
+
subject { packet.to_h }
|
|
77
|
+
it { is_expected.to eq status: 16, v_bat: 49.7, soc: 95, p_pv: 0, p_charge: 0, p_discharge: 633, v_acr: 247.6, f_ac: 50.1, p_inv: 569, p_rec: 0, v_eps: 247.6, f_eps: 50.1, p_to_grid: 0, p_to_user: 0, e_pv_day: 22.1, e_inv_day: 4.2, e_rec_day: 7.3, e_chg_day: 8.6, e_dischg_day: 5.2, e_eps_day: 0.0, e_to_grid_day: 3.6, e_to_user_day: 0.2, v_bus_1: 375.6, v_bus_2: 299.7 }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe LXP::Packet::ReadHold do
|
|
4
|
+
let(:packet) { LXP::Packet::ReadHold.new }
|
|
5
|
+
|
|
6
|
+
before do
|
|
7
|
+
packet.inverter_serial = '1234567890'
|
|
8
|
+
packet.datalog_serial = '0987654321'
|
|
9
|
+
packet.register = 21
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'has the correct attributes' do
|
|
13
|
+
expect(packet).to have_attributes register: 21, values: [1, 0], value: 1,
|
|
14
|
+
to_h: { 21 => 1 },
|
|
15
|
+
protocol: 1,
|
|
16
|
+
packet_length: 32, data_length: 18,
|
|
17
|
+
tcp_function: LXP::Packet::TcpFunctions::TRANSLATED_DATA,
|
|
18
|
+
device_function: LXP::Packet::DeviceFunctions::READ_HOLD,
|
|
19
|
+
inverter_serial: '1234567890',
|
|
20
|
+
datalog_serial: '0987654321',
|
|
21
|
+
bytes: [161, 26, 1, 0, 32, 0, 1, 194, 48, 57, 56, 55, 54, 53, 52, 51, 50, 49, 18, 0, 0, 3, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 21, 0, 1, 0, 241, 72]
|
|
22
|
+
expect(packet[21]).to eq 1
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'creates the correct binary' do
|
|
26
|
+
expect(packet.to_bin).to eq [161, 26, 1, 0, 32, 0, 1, 194, 48, 57, 56, 55, 54, 53, 52, 51, 50, 49, 18, 0, 0, 3, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 21, 0, 1, 0, 241, 72].pack('C*')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'works with protocol 2' do
|
|
30
|
+
packet.protocol = 2
|
|
31
|
+
expect(packet).to have_attributes protocol: 2,
|
|
32
|
+
bytes: [161, 26, 2, 0, 32, 0, 1, 194, 48, 57, 56, 55, 54, 53, 52, 51, 50, 49, 18, 0, 0, 3, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 21, 0, 1, 0, 241, 72]
|
|
33
|
+
end
|
|
34
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe LXP::Packet::WriteSingle do
|
|
4
|
+
let(:packet) { LXP::Packet::WriteSingle.new }
|
|
5
|
+
|
|
6
|
+
before do
|
|
7
|
+
packet.inverter_serial = '1234567890'
|
|
8
|
+
packet.datalog_serial = '0987654321'
|
|
9
|
+
packet.register = 21
|
|
10
|
+
packet.value = 8000
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'has the correct attributes' do
|
|
14
|
+
expect(packet).to have_attributes register: 21, values: [64, 31], value: 8000,
|
|
15
|
+
protocol: 1,
|
|
16
|
+
packet_length: 32, data_length: 18,
|
|
17
|
+
tcp_function: LXP::Packet::TcpFunctions::TRANSLATED_DATA,
|
|
18
|
+
device_function: LXP::Packet::DeviceFunctions::WRITE_SINGLE,
|
|
19
|
+
inverter_serial: '1234567890',
|
|
20
|
+
datalog_serial: '0987654321',
|
|
21
|
+
bytes: [161, 26, 1, 0, 32, 0, 1, 194, 48, 57, 56, 55, 54, 53, 52, 51, 50, 49, 18, 0, 0, 6, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 21, 0, 64, 31, 69, 211]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'creates the correct binary' do
|
|
25
|
+
expect(packet.to_bin).to eq [161, 26, 1, 0, 32, 0, 1, 194, 48, 57, 56, 55, 54, 53, 52, 51, 50, 49, 18, 0, 0, 6, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 21, 0, 64, 31, 69, 211].pack('C*')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'works with protocol 2' do
|
|
29
|
+
packet.protocol = 2
|
|
30
|
+
expect(packet).to have_attributes protocol: 2,
|
|
31
|
+
bytes: [161, 26, 2, 0, 32, 0, 1, 194, 48, 57, 56, 55, 54, 53, 52, 51, 50, 49, 18, 0, 0, 6, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 21, 0, 64, 31, 69, 211]
|
|
32
|
+
end
|
|
33
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lxp-packet
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Elsworth
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-04-
|
|
11
|
+
date: 2020-04-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -24,6 +24,20 @@ dependencies:
|
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
27
41
|
description:
|
|
28
42
|
email:
|
|
29
43
|
- chris@cae.me.uk
|
|
@@ -32,6 +46,8 @@ extensions: []
|
|
|
32
46
|
extra_rdoc_files: []
|
|
33
47
|
files:
|
|
34
48
|
- ".gitignore"
|
|
49
|
+
- ".rspec"
|
|
50
|
+
- ".rubocop.yml"
|
|
35
51
|
- CHANGELOG.md
|
|
36
52
|
- Gemfile
|
|
37
53
|
- LICENSE.txt
|
|
@@ -55,6 +71,10 @@ files:
|
|
|
55
71
|
- lib/lxp/utils.rb
|
|
56
72
|
- lib/lxp/version.rb
|
|
57
73
|
- lxp-packet.gemspec
|
|
74
|
+
- spec/parser_spec.rb
|
|
75
|
+
- spec/read_hold_spec.rb
|
|
76
|
+
- spec/spec_helper.rb
|
|
77
|
+
- spec/write_single_spec.rb
|
|
58
78
|
homepage: https://github.com/celsworth/lxp-packet
|
|
59
79
|
licenses:
|
|
60
80
|
- MIT
|