ruby-sml 0.2
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.
- data/LICENSE +24 -0
- data/README.md +0 -0
- data/lib/ruby-sml.rb +30 -0
- data/lib/ruby-sml/crc16.rb +47 -0
- data/lib/ruby-sml/encoding-binary.rb +262 -0
- data/lib/ruby-sml/helpers.rb +58 -0
- data/lib/ruby-sml/nilclass-mixin.rb +5 -0
- data/lib/ruby-sml/obis.rb +139 -0
- data/lib/ruby-sml/sml-attention.rb +60 -0
- data/lib/ruby-sml/sml-file.rb +37 -0
- data/lib/ruby-sml/sml-getlist.rb +88 -0
- data/lib/ruby-sml/sml-getprocparameter.rb +61 -0
- data/lib/ruby-sml/sml-getprofilelist.rb +103 -0
- data/lib/ruby-sml/sml-getprofilepack.rb +107 -0
- data/lib/ruby-sml/sml-listentry.rb +98 -0
- data/lib/ruby-sml/sml-message.rb +87 -0
- data/lib/ruby-sml/sml-messagebody.rb +95 -0
- data/lib/ruby-sml/sml-periodentry.rb +38 -0
- data/lib/ruby-sml/sml-procparametervalue.rb +46 -0
- data/lib/ruby-sml/sml-profileobjectheaderentry.rb +31 -0
- data/lib/ruby-sml/sml-profileobjectperiodentry.rb +46 -0
- data/lib/ruby-sml/sml-publicclose.rb +51 -0
- data/lib/ruby-sml/sml-publicopen.rb +84 -0
- data/lib/ruby-sml/sml-setprocparameter.rb +37 -0
- data/lib/ruby-sml/sml-time.rb +46 -0
- data/lib/ruby-sml/sml-tree.rb +42 -0
- data/lib/ruby-sml/sml-treepath.rb +24 -0
- data/lib/ruby-sml/sml-tupelentry.rb +105 -0
- data/lib/ruby-sml/sml-valueentry.rb +29 -0
- data/lib/ruby-sml/transport-binary.rb +158 -0
- data/lib/ruby-sml/units.rb +84 -0
- data/sample.rb +66 -0
- metadata +85 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'digest/sha2'
|
2
|
+
require 'openssl'
|
3
|
+
|
4
|
+
require 'ruby-sml/nilclass-mixin'
|
5
|
+
require 'ruby-sml/sml-time'
|
6
|
+
|
7
|
+
module SML
|
8
|
+
|
9
|
+
class ListEntry
|
10
|
+
attr_accessor :name, :status, :status_type, :value_time, :unit, :scaler, :value, :value_type, :signature
|
11
|
+
|
12
|
+
def initialize(name, status, status_type, value_time, unit, scaler, value, value_type, signature)
|
13
|
+
@name = name
|
14
|
+
@status = status
|
15
|
+
@status_type = status_type
|
16
|
+
@value_time = value_time
|
17
|
+
@unit = unit
|
18
|
+
@scaler = scaler
|
19
|
+
@value = value
|
20
|
+
@value_type = value_type
|
21
|
+
@signature = signature
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.construct(array_rep)
|
25
|
+
return nil if array_rep.nil?
|
26
|
+
name = array_rep.shift
|
27
|
+
status = array_rep.shift
|
28
|
+
status_type = array_rep.shift unless status.nil?
|
29
|
+
value_time = SML::Time.construct(array_rep.shift)
|
30
|
+
unit = array_rep.shift
|
31
|
+
array_rep.shift unless unit.nil?
|
32
|
+
scaler = array_rep.shift
|
33
|
+
array_rep.shift unless scaler.nil?
|
34
|
+
value = array_rep.shift
|
35
|
+
case value
|
36
|
+
when Fixnum
|
37
|
+
value_type = array_rep.shift
|
38
|
+
when String
|
39
|
+
value_type = :string
|
40
|
+
end
|
41
|
+
signature = array_rep.shift
|
42
|
+
|
43
|
+
return nil if (value.nil? or name.nil?)
|
44
|
+
return SML::ListEntry.new(name, status, status_type, value_time, unit, scaler, value, value_type, signature)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.pconstruct(o={})
|
48
|
+
return SML::ListEntry.new(o[:name], o[:status], o[:status_type], o[:value_time], o[:unit], o[:scaler], o[:value], o[:value_type], o[:signature])
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_a
|
52
|
+
result = [] << name << status << status_type << value_time.to_a << unit << :uint8 << scaler << :int8 << value
|
53
|
+
result << value_type if value.class == Fixnum
|
54
|
+
return result << signature
|
55
|
+
end
|
56
|
+
|
57
|
+
def calculate_hash(server_id)
|
58
|
+
return nil unless [:int8, :int16, :int32, :int64, :uint8, :uint16, :uint32, :uint64].include?(@value_type)
|
59
|
+
return nil unless @value_time.type == :timestamp
|
60
|
+
return nil unless @name.length == 6
|
61
|
+
|
62
|
+
bytes = String.new
|
63
|
+
bytes += server_id
|
64
|
+
(10-server_id.length).times do
|
65
|
+
bytes += [0x00].pack('c')
|
66
|
+
end
|
67
|
+
bytes += [@value_time.value].pack('N')
|
68
|
+
bytes += [@status].pack('c')
|
69
|
+
bytes += @name
|
70
|
+
bytes += [@unit].pack('c')
|
71
|
+
bytes += [@scaler].pack('c')
|
72
|
+
bytes += [(@value & 0xffff0000) >> 32,(@value & 0x0000ffff)].pack('NN')
|
73
|
+
17.times do
|
74
|
+
bytes += [0x00].pack('c')
|
75
|
+
end
|
76
|
+
|
77
|
+
hash = Digest::SHA2.new(256).digest(bytes)[0,24]
|
78
|
+
|
79
|
+
return hash
|
80
|
+
end
|
81
|
+
def sign(server_id, private_key)
|
82
|
+
hash = calculate_hash(server_id)
|
83
|
+
return nil if hash.nil?
|
84
|
+
|
85
|
+
signature = OpenSSL::PKey::EC.new(private_key).dsa_sign_asn1(hash)
|
86
|
+
|
87
|
+
@signature = signature
|
88
|
+
end
|
89
|
+
def verify(server_id, public_key)
|
90
|
+
hash = calculate_hash(server_id)
|
91
|
+
return nil if hash.nil?
|
92
|
+
|
93
|
+
return OpenSSL::PKey::EC.new(public_key).dsa_verify_asn1(hash,@signature)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'ruby-sml/nilclass-mixin'
|
2
|
+
require 'ruby-sml/sml-messagebody'
|
3
|
+
|
4
|
+
require 'ruby-sml/encoding-binary'
|
5
|
+
|
6
|
+
module SML
|
7
|
+
|
8
|
+
class Message
|
9
|
+
attr_accessor :transaction_id, :group_no, :abort_on_error, :body, :checksum
|
10
|
+
|
11
|
+
def initialize(transaction_id, group_no, abort_on_error, body, checksum)
|
12
|
+
@transaction_id = transaction_id
|
13
|
+
@group_no = group_no
|
14
|
+
@abort_on_error = abort_on_error
|
15
|
+
@body = body
|
16
|
+
@checksum = checksum
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.construct(array_rep)
|
20
|
+
return nil if array_rep.nil?
|
21
|
+
transaction_id = array_rep.shift
|
22
|
+
group_no = array_rep.shift
|
23
|
+
array_rep.shift unless group_no.nil?
|
24
|
+
abort_on_error_code = array_rep.shift
|
25
|
+
array_rep.shift unless abort_on_error_code.nil?
|
26
|
+
abort_on_error = nil
|
27
|
+
abort_on_error = case abort_on_error_code
|
28
|
+
when 0x00
|
29
|
+
:continue
|
30
|
+
when 0x01
|
31
|
+
:continue_with_next_group
|
32
|
+
when 0x02
|
33
|
+
:finish_group
|
34
|
+
when 0xff
|
35
|
+
:abort
|
36
|
+
end
|
37
|
+
body = SML::MessageBody.construct(array_rep.shift)
|
38
|
+
return nil if body.nil?
|
39
|
+
checksum = array_rep.shift
|
40
|
+
array_rep.shift unless checksum.nil?
|
41
|
+
|
42
|
+
return nil unless array_rep.shift == :end_of_message
|
43
|
+
return SML::Message.new(transaction_id, group_no, abort_on_error, body, checksum)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.pconstruct(o={})
|
47
|
+
return SML::Message.new(o[:transaction_id], o[:group_no], o[:abord_on_error], o[:body], o[:checksum])
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_a
|
51
|
+
calculate_checksum
|
52
|
+
return to_a_internal
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_a_internal
|
56
|
+
abort_on_error_code = case abort_on_error
|
57
|
+
when :continue
|
58
|
+
0x00
|
59
|
+
when :continue_with_next_group
|
60
|
+
0x01
|
61
|
+
when :finish_group
|
62
|
+
0x02
|
63
|
+
when :abort
|
64
|
+
0xff
|
65
|
+
end
|
66
|
+
|
67
|
+
result = [] << transaction_id << group_no
|
68
|
+
result << :uint8 unless group_no.nil?
|
69
|
+
result << abort_on_error_code
|
70
|
+
result << :uint8 unless abort_on_error_code.nil?
|
71
|
+
result << SML::MessageBody.to_a(body) << checksum
|
72
|
+
result << :uint16 unless checksum.nil?
|
73
|
+
return result << :end_of_message
|
74
|
+
end
|
75
|
+
|
76
|
+
def calculate_checksum
|
77
|
+
encoded = SML::BinaryEncoding.encode_value(self.to_a_internal, :array)
|
78
|
+
encoded.slice!(-3,3)
|
79
|
+
calculated_checksum = CRC16.crc16(encoded)
|
80
|
+
calculated_checksum = (calculated_checksum ^ 0xffff)
|
81
|
+
calculated_checksum = ((calculated_checksum & 0xff00) >> 8) | ((calculated_checksum & 0x00ff) << 8)
|
82
|
+
@checksum = calculated_checksum
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'ruby-sml/nilclass-mixin'
|
2
|
+
require 'ruby-sml/sml-publicopen'
|
3
|
+
require 'ruby-sml/sml-publicclose'
|
4
|
+
require 'ruby-sml/sml-getprofilepack'
|
5
|
+
require 'ruby-sml/sml-getprofilelist'
|
6
|
+
require 'ruby-sml/sml-getprocparameter'
|
7
|
+
require 'ruby-sml/sml-setprocparameter'
|
8
|
+
require 'ruby-sml/sml-getlist'
|
9
|
+
require 'ruby-sml/sml-attention'
|
10
|
+
|
11
|
+
module SML
|
12
|
+
|
13
|
+
class MessageBody
|
14
|
+
|
15
|
+
def self.construct(array_rep)
|
16
|
+
return nil if array_rep.nil?
|
17
|
+
choice = array_rep.shift
|
18
|
+
array_rep.shift unless choice.nil?
|
19
|
+
body_array = array_rep.shift
|
20
|
+
return case choice
|
21
|
+
when 0x0100
|
22
|
+
SML::PublicOpen::Request.construct(body_array)
|
23
|
+
when 0x0101
|
24
|
+
SML::PublicOpen::Response.construct(body_array)
|
25
|
+
when 0x0200
|
26
|
+
SML::PublicClose::Request.construct(body_array)
|
27
|
+
when 0x0201
|
28
|
+
SML::PublicClose::Response.construct(body_array)
|
29
|
+
when 0x0300
|
30
|
+
SML::GetProfilePack::Request.construct(body_array)
|
31
|
+
when 0x0301
|
32
|
+
SML::GetProfilePack::Response.construct(body_array)
|
33
|
+
when 0x0400
|
34
|
+
SML::GetProfileList::Request.construct(body_array)
|
35
|
+
when 0x0401
|
36
|
+
SML::GetProfileList::Response.construct(body_array)
|
37
|
+
when 0x0500
|
38
|
+
SML::GetProcParameter::Request.construct(body_array)
|
39
|
+
when 0x0501
|
40
|
+
SML::GetProcParameter::Response.construct(body_array)
|
41
|
+
when 0x0600
|
42
|
+
SML::SetProcParameter::Request.construct(body_array)
|
43
|
+
when 0x0601
|
44
|
+
nil
|
45
|
+
# SML::SetProcParameter::Response.construct(body_array)
|
46
|
+
# SML_SetProcParameter.Res is supposed to exist, but the standard doesn't define it
|
47
|
+
# respones to SetProcParameter.Req are supposed to be SML_Attention.Res'
|
48
|
+
when 0x0700
|
49
|
+
SML::GetList::Request.construct(body_array)
|
50
|
+
when 0x0701
|
51
|
+
SML::GetList::Response.construct(body_array)
|
52
|
+
when 0xff01
|
53
|
+
SML::Attention::Response.construct(body_array)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
def self.to_a(object)
|
57
|
+
choice = case object
|
58
|
+
when SML::PublicOpen::Request
|
59
|
+
0x0100
|
60
|
+
when SML::PublicOpen::Response
|
61
|
+
0x0101
|
62
|
+
when SML::PublicClose::Request
|
63
|
+
0x0200
|
64
|
+
when SML::PublicClose::Response
|
65
|
+
0x0201
|
66
|
+
when SML::GetProfilePack::Request
|
67
|
+
0x0300
|
68
|
+
when SML::GetProfilePack::Response
|
69
|
+
0x0301
|
70
|
+
when SML::GetProfileList::Request
|
71
|
+
0x0400
|
72
|
+
when SML::GetProfileList::Response
|
73
|
+
0x0401
|
74
|
+
when SML::GetProcParameter::Request
|
75
|
+
0x0500
|
76
|
+
when SML::GetProcParameter::Response
|
77
|
+
0x0501
|
78
|
+
when SML::SetProcParameter::Request
|
79
|
+
0x0600
|
80
|
+
when SML::GetList::Request
|
81
|
+
0x0700
|
82
|
+
when SML::GetList::Response
|
83
|
+
0x0701
|
84
|
+
when SML::Attention::Response
|
85
|
+
0xff01
|
86
|
+
end
|
87
|
+
|
88
|
+
result = [] << choice
|
89
|
+
result << :uint32 unless choice.nil?
|
90
|
+
return result << object.to_a
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'ruby-sml/nilclass-mixin'
|
2
|
+
|
3
|
+
module SML
|
4
|
+
|
5
|
+
class PeriodEntry
|
6
|
+
attr_accessor :name, :unit, :scaler, :value, :value_type, :signature
|
7
|
+
|
8
|
+
def initialize(name, unit, scaler, value, value_type, signature)
|
9
|
+
@name = name
|
10
|
+
@unit = unit
|
11
|
+
@scaler = scaler
|
12
|
+
@value = value
|
13
|
+
@value_type = value_type
|
14
|
+
@signature = signature
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.construct(array_rep)
|
18
|
+
return nil if array_rep.nil?
|
19
|
+
name = array_rep.shift
|
20
|
+
unit = array_rep.shift
|
21
|
+
scaler = array_rep.shift
|
22
|
+
array_rep.shift unless scaler.nil?
|
23
|
+
value = array_rep.shift
|
24
|
+
value_type = array_rep.shift
|
25
|
+
signature = array_rep.shift
|
26
|
+
|
27
|
+
return nil if value.nil?
|
28
|
+
return SML::PeriodEntry.new(name, unit, scaler, value, value_type, signature)
|
29
|
+
end
|
30
|
+
def to_a
|
31
|
+
result = [] << name << unit << scaler
|
32
|
+
result << :int8 unless scaler.nil?
|
33
|
+
return result << value << value_type << signature
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'ruby-sml/nilclass-mixin'
|
2
|
+
require 'ruby-sml/sml-periodentry'
|
3
|
+
require 'ruby-sml/sml-tupelentry'
|
4
|
+
require 'ruby-sml/sml-time'
|
5
|
+
|
6
|
+
module SML
|
7
|
+
|
8
|
+
class ProcParameterValue
|
9
|
+
|
10
|
+
def self.construct(array_rep)
|
11
|
+
return nil if array_rep.nil?
|
12
|
+
choice = array_rep.shift
|
13
|
+
array_rep.shift unless choice.nil?
|
14
|
+
body_rep = array_rep.shift
|
15
|
+
|
16
|
+
return case choice
|
17
|
+
when 0x01
|
18
|
+
body_rep
|
19
|
+
when 0x02
|
20
|
+
SML::PeriodEntry.construct(body_rep)
|
21
|
+
when 0x03
|
22
|
+
SML::TupelEntry.construct(body_rep)
|
23
|
+
when 0x04
|
24
|
+
SML::Time.construct(body_rep)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
def self.to_a(object)
|
28
|
+
choice = case object
|
29
|
+
when SML::PeriodEntry
|
30
|
+
0x02
|
31
|
+
when SML::TupelEntry
|
32
|
+
0x03
|
33
|
+
when SML::Time
|
34
|
+
0x04
|
35
|
+
else
|
36
|
+
0x01
|
37
|
+
end
|
38
|
+
|
39
|
+
result = [] << choice
|
40
|
+
result << :uint8 unless choice.nil?
|
41
|
+
return result << object
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'ruby-sml/nilclass-mixin'
|
2
|
+
|
3
|
+
module SML
|
4
|
+
|
5
|
+
class ProfileObjectHeaderEntry
|
6
|
+
attr_accessor :name, :unit, :scaler
|
7
|
+
|
8
|
+
def initialize(name, unit, scaler)
|
9
|
+
@name = name
|
10
|
+
@unit = unit
|
11
|
+
@scaler = scaler
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.construct(array_rep)
|
15
|
+
return nil if array_rep.nil?
|
16
|
+
name = array_rep.shift
|
17
|
+
unit = array_rep.shift
|
18
|
+
scaler = array_rep.shift
|
19
|
+
array_rep.shift unless scaler.nil?
|
20
|
+
|
21
|
+
return SML::ProfileObjectHeaderEntry.new(name, unit, scaler)
|
22
|
+
end
|
23
|
+
def to_a
|
24
|
+
result = [] << name << unit << scaler
|
25
|
+
result << :int8 unless scaler.nil?
|
26
|
+
return result
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'ruby-sml/nilclass-mixin'
|
2
|
+
require 'ruby-sml/sml-time'
|
3
|
+
require 'ruby-sml/sml-valueentry'
|
4
|
+
|
5
|
+
module SML
|
6
|
+
|
7
|
+
class ProfileObjectPeriodEntry
|
8
|
+
attr_accessor :val_time, :status, :value_list, :period_signature
|
9
|
+
|
10
|
+
def initialize(val_time, status, value_list, period_signature)
|
11
|
+
@val_time = val_time
|
12
|
+
@status = status
|
13
|
+
@value_list = value_list
|
14
|
+
@period_signature = period_signature
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.construct(array_rep)
|
18
|
+
return nil if array_rep.nil?
|
19
|
+
val_time = SML::Time.construct(array_rep.shift)
|
20
|
+
status = array_rep.shift
|
21
|
+
array_rep.shift unless status.nil?
|
22
|
+
value_list = []
|
23
|
+
array_rep.shift.each do |entry_array_rep|
|
24
|
+
entry = SML::ValueEntry.construct(entry_array_rep)
|
25
|
+
return nil if entry.nil?
|
26
|
+
value_list << entry
|
27
|
+
end
|
28
|
+
period_signature = array_rep.shift
|
29
|
+
|
30
|
+
return nil if val_time.nil?
|
31
|
+
return SML::ProfileObjectPeriodEntry.new(val_time, status, value_list, period_signature)
|
32
|
+
end
|
33
|
+
def to_a
|
34
|
+
value_list_array = []
|
35
|
+
value_list.each do |entry|
|
36
|
+
value_list_array << entry.to_a
|
37
|
+
end
|
38
|
+
|
39
|
+
result = [] << val_time.to_a << status
|
40
|
+
result << :uint64 unless status.nil?
|
41
|
+
return result << value_list_array << period_signature
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|