automotive-ecu 0.1.10 → 0.2.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.
- checksums.yaml +4 -4
- data/bin/sandbox +16 -0
- data/lib/ecu/combined_list.rb +9 -0
- data/lib/ecu/interfaces/dcm/dcm_lexer.rb +78 -0
- data/lib/ecu/interfaces/dcm/dcm_parser.rb +215 -0
- data/lib/ecu/interfaces/dcm/dcm_parser_error.rb +35 -0
- data/lib/ecu/interfaces/dcm/festkennfeld.rb +0 -3
- data/lib/ecu/interfaces/dcm/festkennlinie.rb +0 -3
- data/lib/ecu/interfaces/dcm/festwert.rb +0 -4
- data/lib/ecu/interfaces/dcm/festwerteblock.rb +0 -4
- data/lib/ecu/interfaces/dcm/gruppenkennfeld.rb +0 -3
- data/lib/ecu/interfaces/dcm/gruppenkennlinie.rb +0 -3
- data/lib/ecu/interfaces/dcm/kennfeld.rb +0 -4
- data/lib/ecu/interfaces/dcm/kennlinie.rb +0 -4
- data/lib/ecu/interfaces/dcm/label.rb +2 -20
- data/lib/ecu/interfaces/dcm/label_list.rb +11 -56
- data/lib/ecu/interfaces/dcm/stuetzstellenverteilung.rb +0 -4
- data/lib/ecu/interfaces/json/label_list.rb +9 -0
- data/lib/ecu/interfaces/lab/combined_list.rb +1 -1
- data/lib/ecu/interfaces/lab/lab_lexer.rb +57 -0
- data/lib/ecu/interfaces/lab/lab_parser.rb +119 -48
- data/lib/ecu/interfaces/lab/lab_parser_error.rb +38 -0
- data/lib/ecu/interfaces/lab/label.rb +0 -14
- data/lib/ecu/interfaces/lab/label_list.rb +1 -1
- data/lib/ecu/interfaces/lab/signal.rb +0 -8
- data/lib/ecu/interfaces/lab/signal_list.rb +1 -1
- data/lib/ecu/labels/label.rb +9 -1
- data/lib/ecu/labels/label_list.rb +1 -0
- data/lib/ecu/version.rb +1 -1
- metadata +9 -6
- data/lib/ecu/interfaces/dcm/buffer.rb +0 -39
- data/lib/ecu/interfaces/dcm/functions.rb +0 -13
- data/lib/ecu/interfaces/dcm/malformed_dcm_error.rb +0 -34
- data/lib/ecu/interfaces/dcm/property_parser.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75ae19b7bbf77cd45e42cc9e6b5138d19e85101df900fe844e3102bf18ac2c79
|
4
|
+
data.tar.gz: ecbc4929534d9245169ca0eaa7518df4fb18a08727b66244f4b6382a053af827
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff02d2b04386b8129d249be984934bcda8d53bd5f24057541f36c555152cbda2b008ac9ee1d4bdfec90d67fdcbc2c0455c0eaac2d5df524f44b857d21cad0cf2
|
7
|
+
data.tar.gz: fef42ccdfe5947851955aad5fc2655cabd047576268310da2cc1b6606e3c4390f6db91907a21b24710140149dfafd8b74670ac2aa701009a9ba8c9f4625d872b
|
data/bin/sandbox
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative "../lib/ecu/combined_list"
|
4
|
+
|
5
|
+
begin
|
6
|
+
list = Ecu::CombinedList.from_file('spec/fixtures/lab/advanced.lab')
|
7
|
+
list.labels.each do |item|
|
8
|
+
puts item
|
9
|
+
end
|
10
|
+
list.signals.each do |item|
|
11
|
+
puts item.description
|
12
|
+
end
|
13
|
+
rescue Ecu::LabParserError => e
|
14
|
+
puts e.message
|
15
|
+
puts e.context
|
16
|
+
end
|
data/lib/ecu/combined_list.rb
CHANGED
@@ -6,6 +6,15 @@ require_relative "interfaces/lab/combined_list"
|
|
6
6
|
class Ecu
|
7
7
|
class CombinedList
|
8
8
|
|
9
|
+
def self.from_file(file_path)
|
10
|
+
case File.extname(file_path)
|
11
|
+
when ".lab" then self.from_lab(File.read_encoded(file_path))
|
12
|
+
when ".a2l" then self.from_a2l(File.read_encoded(file_path))
|
13
|
+
when ".dbc" then self.from_dbc(File.read_encoded(file_path))
|
14
|
+
else fail "Unknown file extension: #{file_path}!"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
9
18
|
def initialize(signals=SignalList.new, labels=LabelList.new, headers=[], subheaders=[])
|
10
19
|
@signals = signals
|
11
20
|
@labels = labels
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require "strscan"
|
2
|
+
|
3
|
+
class Ecu
|
4
|
+
class LabelList
|
5
|
+
class DcmLexer
|
6
|
+
attr_reader :doc
|
7
|
+
def initialize(doc)
|
8
|
+
@doc = doc
|
9
|
+
@scan = StringScanner.new(doc)
|
10
|
+
end
|
11
|
+
|
12
|
+
KEYWORDS = [
|
13
|
+
"FUNKTIONEN", "FESTWERT", "FESTWERTEBLOCK", "KENNLINIE", "GRUPPENKENNLINIE", "FESTKENNLINIE",
|
14
|
+
"KENNFELD", "GRUPPENKENNFELD", "FESTKENNFELD", "STUETZSTELLENVERTEILUNG",
|
15
|
+
"FKT",
|
16
|
+
"ST/X", "ST/Y", "WERT", "ST_TX/X", "ST_TX/Y", "TEXT",
|
17
|
+
"FUNKTION",
|
18
|
+
"EINHEIT_X", "EINHEIT_Y", "EINHEIT_W",
|
19
|
+
"LANGNAME", "DISPLAYNAME",
|
20
|
+
"END",
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
HEADER = "KONSERVIERUNG_FORMAT 2.0"
|
24
|
+
WHITESPACE = %r{ [ \t]+ }x
|
25
|
+
NEWLINE = %r{ \r\n|\n }x
|
26
|
+
COMMENT = %r{ ^\*.*$ }x
|
27
|
+
DIMENSIONS_SEP = %r{ @ }x
|
28
|
+
QUOTED_TEXT = %r{ "[^"]*" }x
|
29
|
+
IDENTIFIER = %r{ [A-Za-z][A-Za-z0-9_\.]* }x
|
30
|
+
UNSIGNED_INT = %r{ [0]|[1-9][0-9]* }x
|
31
|
+
INT = %r{ [-]?#{UNSIGNED_INT} }x
|
32
|
+
FLOAT_EXP = %r{ [eE][+-]?[0-9]+ }x
|
33
|
+
FLOAT = %r{
|
34
|
+
[-+]?
|
35
|
+
(?:
|
36
|
+
#{UNSIGNED_INT}?[.][0-9]+#{FLOAT_EXP} | # 1.23e10 or .45e3
|
37
|
+
#{UNSIGNED_INT} #{FLOAT_EXP} | # 3e4
|
38
|
+
#{UNSIGNED_INT}?[.][0-9]+ | # 7.3 or .50
|
39
|
+
#{UNSIGNED_INT} [.][0-9]* # 3.02 or 9.
|
40
|
+
)
|
41
|
+
}x
|
42
|
+
KW_RE = /#{Regexp.union(KEYWORDS.sort)}\b/
|
43
|
+
KW_TABLE = Hash[KEYWORDS.map { [_1, _1.upcase.to_sym] }]
|
44
|
+
|
45
|
+
|
46
|
+
def next_token
|
47
|
+
@scan.skip(WHITESPACE)
|
48
|
+
|
49
|
+
return if @scan.eos?
|
50
|
+
|
51
|
+
case
|
52
|
+
when @scan.skip(NEWLINE) then :NEWLINE
|
53
|
+
when s = @scan.scan(KW_RE) then KW_TABLE[s]
|
54
|
+
when @scan.skip(QUOTED_TEXT) then :QUOTED_TEXT
|
55
|
+
when @scan.skip(COMMENT) then :COMMENT
|
56
|
+
when @scan.skip(DIMENSIONS_SEP) then :DIMENSIONS_SEP
|
57
|
+
when @scan.skip(HEADER) then :HEADER
|
58
|
+
when @scan.skip(IDENTIFIER) then :IDENTIFIER
|
59
|
+
when @scan.skip(FLOAT) then :FLOAT
|
60
|
+
when @scan.skip(INT) then :INT
|
61
|
+
|
62
|
+
else
|
63
|
+
@scan.getch
|
64
|
+
:UNKNOWN_CHAR
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def token_value
|
69
|
+
@doc.byteslice(@scan.pos - @scan.matched_size, @scan.matched_size)
|
70
|
+
end
|
71
|
+
|
72
|
+
def lineno
|
73
|
+
@doc.byteslice(0, @scan.pos).count("\n") +
|
74
|
+
(@scan.beginning_of_line? ? 0 : 1)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
require_relative "dcm_lexer"
|
2
|
+
require_relative "dcm_parser_error"
|
3
|
+
|
4
|
+
class Ecu
|
5
|
+
class LabelList
|
6
|
+
class DcmParser
|
7
|
+
|
8
|
+
ARY_PROPERTIES = %i( xvalue yvalue value )
|
9
|
+
KEY_MAPPING = {
|
10
|
+
"ST/X": :xvalue,
|
11
|
+
"ST/Y": :yvalue,
|
12
|
+
"WERT": :value,
|
13
|
+
"ST_TX/X": :xvalue,
|
14
|
+
"ST_TX/Y": :yvalue,
|
15
|
+
"TEXT": :value,
|
16
|
+
"FUNKTION": :function,
|
17
|
+
"DISPLAYNAME": :displayname,
|
18
|
+
"EINHEIT_X": :xunit,
|
19
|
+
"EINHEIT_Y": :yunit,
|
20
|
+
"EINHEIT_W": :unit,
|
21
|
+
"LANGNAME": :description,
|
22
|
+
}
|
23
|
+
|
24
|
+
attr_reader :lexer, :state, :labels, :headers, :subheaders
|
25
|
+
def initialize(doc)
|
26
|
+
@lexer = DcmLexer.new(doc)
|
27
|
+
@state = :PRE_HEADER
|
28
|
+
@labels = []
|
29
|
+
@headers = []
|
30
|
+
@subheaders = []
|
31
|
+
reset_label!
|
32
|
+
end
|
33
|
+
|
34
|
+
def call
|
35
|
+
while tok = lexer.next_token
|
36
|
+
begin
|
37
|
+
case @state
|
38
|
+
in :PRE_HEADER
|
39
|
+
case tok
|
40
|
+
in :HEADER then next_state(:POST_HEADER)
|
41
|
+
in :COMMENT then add_header(lexer.token_value)
|
42
|
+
in :NEWLINE then # noop
|
43
|
+
end
|
44
|
+
in :POST_HEADER
|
45
|
+
case tok
|
46
|
+
in :FUNKTIONEN then next_state(:FUNCTIONS_CONTENT)
|
47
|
+
in :FESTWERT then start_label(Festwert)
|
48
|
+
in :FESTWERTEBLOCK then start_label(Festwerteblock)
|
49
|
+
in :KENNLINIE then start_label(Kennlinie)
|
50
|
+
in :GRUPPENKENNLINIE then start_label(Gruppenkennlinie)
|
51
|
+
in :FESTKENNLINIE then start_label(Festkennlinie)
|
52
|
+
in :KENNFELD then start_label(Kennfeld)
|
53
|
+
in :GRUPPENKENNFELD then start_label(Gruppenkennfeld)
|
54
|
+
in :FESTKENNFELD then start_label(Festkennfeld)
|
55
|
+
in :STUETZSTELLENVERTEILUNG then start_label(Stuetzstellenverteilung)
|
56
|
+
in :COMMENT then add_subheader(lexer.token_value)
|
57
|
+
in :NEWLINE then # noop
|
58
|
+
end
|
59
|
+
in :FUNCTIONS_CONTENT
|
60
|
+
case tok
|
61
|
+
in :FKT then next_state(:FUNCTION_CONTENT)
|
62
|
+
in :NEWLINE then # noop
|
63
|
+
in :END then next_state(:POST_HEADER)
|
64
|
+
end
|
65
|
+
in :FUNCTION_CONTENT
|
66
|
+
case tok
|
67
|
+
in :IDENTIFIER then # append that shit?
|
68
|
+
in :QUOTED_TEXT then # append that shit as well?
|
69
|
+
in :NEWLINE then next_state(:FUNCTIONS_CONTENT)
|
70
|
+
end
|
71
|
+
in :LABEL_HEADLINE
|
72
|
+
case tok
|
73
|
+
in :IDENTIFIER then add_name(lexer.token_value)
|
74
|
+
in :DIMENSIONS_SEP then # noop, order handled by #add_dimension
|
75
|
+
in :INT then add_dimension(lexer.token_value.to_i)
|
76
|
+
in :NEWLINE then next_state(:LABEL_CONTENT)
|
77
|
+
end
|
78
|
+
in :LABEL_CONTENT
|
79
|
+
case tok
|
80
|
+
in :WERT then start_property(:WERT, :NUMERIC_PROPERTY)
|
81
|
+
in :TEXT then start_property(:TEXT, :TEXT_PROPERTY)
|
82
|
+
in :EINHEIT_X then start_property(:EINHEIT_X, :TEXT_PROPERTY)
|
83
|
+
in :EINHEIT_Y then start_property(:EINHEIT_Y, :TEXT_PROPERTY)
|
84
|
+
in :EINHEIT_W then start_property(:EINHEIT_W, :TEXT_PROPERTY)
|
85
|
+
in :LANGNAME then start_property(:LANGNAME, :TEXT_PROPERTY)
|
86
|
+
in :FUNKTION then start_property(:FUNKTION, :ID_PROPERTY)
|
87
|
+
in :DISPLAYNAME then start_property(:DISPLAYNAME, :ID_PROPERTY)
|
88
|
+
in :END then labels << finish_label
|
89
|
+
in :"ST/X" then start_property(:"ST/X", :NUMERIC_PROPERTY)
|
90
|
+
in :"ST/Y" then start_property(:"ST/Y", :NUMERIC_PROPERTY)
|
91
|
+
in :"ST_TX/X" then start_property(:"ST_TX/X", :TEXT_PROPERTY)
|
92
|
+
in :"ST_TX/Y" then start_property(:"ST_TX/Y", :TEXT_PROPERTY)
|
93
|
+
in :NEWLINE then # noop, DCM badly formatted
|
94
|
+
in :COMMENT then # noop, some programs add SST in comments
|
95
|
+
end
|
96
|
+
in :NUMERIC_PROPERTY
|
97
|
+
case tok
|
98
|
+
in :FLOAT then append_property(lexer.token_value.to_f)
|
99
|
+
in :INT then append_property(lexer.token_value.to_i)
|
100
|
+
in :NEWLINE then finish_property
|
101
|
+
end
|
102
|
+
in :TEXT_PROPERTY
|
103
|
+
case tok
|
104
|
+
in :QUOTED_TEXT then append_property(lexer.token_value[1..-2])
|
105
|
+
in :NEWLINE then finish_property
|
106
|
+
end
|
107
|
+
in :ID_PROPERTY
|
108
|
+
case tok
|
109
|
+
in :IDENTIFIER then append_property(lexer.token_value)
|
110
|
+
in :NEWLINE then finish_property
|
111
|
+
end
|
112
|
+
end
|
113
|
+
rescue NoMatchingPatternError => e
|
114
|
+
raise DcmParserError.new("Unexpected token #{debug_token(tok)} (state: #{state})", lexer)
|
115
|
+
rescue StandardError => e
|
116
|
+
raise DcmParserError.new("#{e.message} (state: #{@state})", lexer)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
[labels, headers, subheaders]
|
120
|
+
end
|
121
|
+
|
122
|
+
def debug_token(tok)
|
123
|
+
if tok == :UNKNOWN_CHAR
|
124
|
+
"UNKNOWN_CHAR: #{lexer.token_value}"
|
125
|
+
else
|
126
|
+
tok
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def next_state(newstate)
|
131
|
+
@state = newstate
|
132
|
+
yield if block_given?
|
133
|
+
end
|
134
|
+
|
135
|
+
def add_header(str)
|
136
|
+
@headers << str[1..].strip
|
137
|
+
end
|
138
|
+
|
139
|
+
def add_subheader(str)
|
140
|
+
@subheaders << str[1..].strip
|
141
|
+
end
|
142
|
+
|
143
|
+
def start_label(constructor)
|
144
|
+
next_state(:LABEL_HEADLINE) do
|
145
|
+
@constructor = constructor
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def finish_label
|
150
|
+
next_state(:POST_HEADER) do
|
151
|
+
@properties
|
152
|
+
.except(:displayname)
|
153
|
+
.then { @constructor.new(**_1) }
|
154
|
+
.tap { reset_label! }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def add_name(str)
|
159
|
+
fail "Duplicate name" if @properties.key?(:name)
|
160
|
+
|
161
|
+
@properties[:name] = str
|
162
|
+
end
|
163
|
+
|
164
|
+
def add_dimension(n)
|
165
|
+
fail "Wrong order name/dimensions" unless @properties.key?(:name)
|
166
|
+
fail "Too many dimensions" if @properties.key?(:xdim) && @properties.key?(:ydim)
|
167
|
+
|
168
|
+
if @properties.key?(:xdim)
|
169
|
+
@properties[:ydim] = n
|
170
|
+
else
|
171
|
+
@properties[:xdim] = n
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def start_property(key, newstate)
|
176
|
+
next_state(newstate) do
|
177
|
+
@key = KEY_MAPPING[key]
|
178
|
+
@value = ARY_PROPERTIES.include?(@key) ? [] : nil
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def append_property(value)
|
183
|
+
# TODO: Check if constructor allows array values
|
184
|
+
if ARY_PROPERTIES.include?(@key)
|
185
|
+
@value << value
|
186
|
+
else
|
187
|
+
fail "Multiple definitions for #{@key}" if @value
|
188
|
+
|
189
|
+
@value = value
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def finish_property
|
194
|
+
next_state(:LABEL_CONTENT) do
|
195
|
+
@properties.merge!({ @key => @value }, &method(:merge_property))
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def merge_property(key, old, new)
|
200
|
+
fail "Multiple property lines not allowed for #{key}" unless ARY_PROPERTIES.include?(key)
|
201
|
+
|
202
|
+
old + new
|
203
|
+
end
|
204
|
+
|
205
|
+
def reset_label!
|
206
|
+
@constructor = nil
|
207
|
+
@properties = {}
|
208
|
+
@key = nil
|
209
|
+
@value = nil
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Ecu
|
2
|
+
class DcmParserError < StandardError
|
3
|
+
|
4
|
+
CTXLENGHT = 5
|
5
|
+
|
6
|
+
attr_reader :doc, :lineno
|
7
|
+
def initialize(msg, lexer)
|
8
|
+
@msg = msg
|
9
|
+
@doc = lexer.doc
|
10
|
+
@lineno = lexer.lineno
|
11
|
+
end
|
12
|
+
|
13
|
+
def message = @msg
|
14
|
+
def context
|
15
|
+
@doc
|
16
|
+
.lines[ctx_startline..ctx_endline]
|
17
|
+
.each
|
18
|
+
.with_index(ctx_startline + 1)
|
19
|
+
.map { |line, n| present(line, n, n == lineno) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def present(line, n, highlight)
|
23
|
+
case highlight
|
24
|
+
when true then fmt(n) + " => | " + line
|
25
|
+
when false then fmt(n) + " | " + line
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def ctx_startline = [0, lineno - CTXLENGHT].max
|
30
|
+
def ctx_endline = @endline ||= [doc.lines.count - 1, lineno + CTXLENGHT].min
|
31
|
+
def fmt_str = "%#{(ctx_endline + 1).to_s.length}d"
|
32
|
+
def fmt(n) = fmt_str % n
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -2,10 +2,6 @@ require_relative "../../../core_ext"
|
|
2
2
|
|
3
3
|
class Ecu
|
4
4
|
class Festwerteblock < Label
|
5
|
-
def self.dcm_header
|
6
|
-
%r{^FESTWERTEBLOCK\s+(?<name>[A-Za-z0-9\._]+)\s+(?<xdim>\d+)(?:\s+@\s+(?<ydim>\d+))?}
|
7
|
-
end
|
8
|
-
|
9
5
|
def to_dcm(indented=false)
|
10
6
|
fmtstr = indented ? "%-25s%s %s" : "%s %s %d"
|
11
7
|
|
@@ -1,25 +1,7 @@
|
|
1
|
-
require_relative "property_parser"
|
2
|
-
|
3
1
|
class Ecu
|
4
2
|
class Label
|
5
|
-
def
|
6
|
-
|
7
|
-
in Array then input
|
8
|
-
in String then input.lines
|
9
|
-
end
|
10
|
-
hsh = dcm_header.extract_captures(lines.first)
|
11
|
-
|
12
|
-
hsh[:xdim] = hsh[:xdim].to_i if hsh.key?(:xdim)
|
13
|
-
hsh[:ydim] = hsh[:ydim].to_i if hsh.key?(:ydim)
|
14
|
-
|
15
|
-
fail "Malformed DCM" if hsh.empty?
|
16
|
-
fail "Malformed DCM" unless lines.last.match?(/^END$/)
|
17
|
-
|
18
|
-
lines[1..-2]
|
19
|
-
.map { DcmPropertyParser.call(_1) }
|
20
|
-
.each { hsh.merge!(_1) { |_, old, new| old + new } }
|
21
|
-
|
22
|
-
new(**hsh)
|
3
|
+
def to_dcm
|
4
|
+
fail "To be defined by child classes"
|
23
5
|
end
|
24
6
|
end
|
25
7
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require_relative "functions"
|
2
1
|
require_relative "festwert"
|
3
2
|
require_relative "festwerteblock"
|
4
3
|
require_relative "kennlinie"
|
@@ -8,69 +7,25 @@ require_relative "kennfeld"
|
|
8
7
|
require_relative "gruppenkennfeld"
|
9
8
|
require_relative "festkennfeld"
|
10
9
|
require_relative "stuetzstellenverteilung"
|
11
|
-
|
12
|
-
require_relative "
|
10
|
+
|
11
|
+
require_relative "dcm_parser"
|
13
12
|
|
14
13
|
class Ecu
|
15
14
|
class LabelList
|
16
15
|
|
17
|
-
DCM_HEADER
|
18
|
-
BLANKLINE_REGEX = /^\s*$/
|
19
|
-
COMMENT_REGEX = /^\*.*/
|
16
|
+
DCM_HEADER = "KONSERVIERUNG_FORMAT 2.0"
|
20
17
|
|
21
18
|
def self.from_dcm(str)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
functions = []
|
26
|
-
labels = {}
|
27
|
-
|
28
|
-
str.each_line.lazy.with_index(1).each do |line, n|
|
29
|
-
line = normalize_whitespace(line)
|
30
|
-
case line
|
31
|
-
when BLANKLINE_REGEX then next
|
32
|
-
when COMMENT_REGEX
|
33
|
-
case buffer.header
|
34
|
-
when :pre then headers << line[1..].strip
|
35
|
-
when :after then subheaders << line[1..].strip
|
36
|
-
when :done then # Header time over, do nothing
|
37
|
-
end
|
38
|
-
when DCM_HEADER then buffer.header_seen!
|
39
|
-
when Functions.dcm_header then buffer.start!(Functions, [line])
|
40
|
-
when Festwert.dcm_header then buffer.start!(Festwert, [line])
|
41
|
-
when Festwerteblock.dcm_header then buffer.start!(Festwerteblock, [line])
|
42
|
-
when Kennlinie.dcm_header then buffer.start!(Kennlinie, [line])
|
43
|
-
when Gruppenkennlinie.dcm_header then buffer.start!(Gruppenkennlinie, [line])
|
44
|
-
when Festkennlinie.dcm_header then buffer.start!(Festkennlinie, [line])
|
45
|
-
when Kennfeld.dcm_header then buffer.start!(Kennfeld, [line])
|
46
|
-
when Gruppenkennfeld.dcm_header then buffer.start!(Gruppenkennfeld, [line])
|
47
|
-
when Festkennfeld.dcm_header then buffer.start!(Festkennfeld, [line])
|
48
|
-
when Stuetzstellenverteilung.dcm_header then buffer.start!(Stuetzstellenverteilung, [line])
|
49
|
-
when "END" then
|
50
|
-
case obj = buffer.finish!(line)
|
51
|
-
when Label
|
52
|
-
fail "Duplicate label #{obj.name}" unless labels[obj.name].nil?
|
53
|
-
|
54
|
-
labels[obj.name] = obj
|
55
|
-
when Functions
|
56
|
-
fail "Duplicate functions definition" unless functions.empty?
|
57
|
-
|
58
|
-
functions = obj
|
59
|
-
else
|
60
|
-
fail "Unknown object #{obj}"
|
61
|
-
end
|
62
|
-
else
|
63
|
-
buffer.append!(line)
|
64
|
-
end
|
65
|
-
rescue StandardError => e
|
66
|
-
raise MalformedDcmError.new(e.message, n, str), e.message
|
67
|
-
end
|
68
|
-
|
69
|
-
new(labels.values, headers, subheaders, true)
|
19
|
+
DcmParser.new(str)
|
20
|
+
.then { _1.call }
|
21
|
+
.then { new(*_1, true) }
|
70
22
|
end
|
71
23
|
|
72
|
-
def self.
|
73
|
-
|
24
|
+
def self.from_dcm_partial(str)
|
25
|
+
str
|
26
|
+
.then { DCM_HEADER + "\n\n" + _1 }
|
27
|
+
.then { from_dcm(_1) }
|
28
|
+
.first
|
74
29
|
end
|
75
30
|
|
76
31
|
def to_dcm(indented=false)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "strscan"
|
2
|
+
|
3
|
+
class Ecu
|
4
|
+
class LabLexer
|
5
|
+
attr_reader :doc
|
6
|
+
def initialize(doc)
|
7
|
+
@doc = doc
|
8
|
+
@scan = StringScanner.new(doc)
|
9
|
+
end
|
10
|
+
|
11
|
+
KEYWORDS = [
|
12
|
+
"[SETTINGS]",
|
13
|
+
"[RAMCELL]",
|
14
|
+
"[Label]",
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
NEWLINE = %r{ \r\n|\n }x
|
18
|
+
SETTINGS_HEADER = %r{ \[SETTINGS\]\s*#{NEWLINE} }x
|
19
|
+
LABELS_HEADER = %r{ \[Label\]\s*#{NEWLINE} }x
|
20
|
+
SIGNALS_HEADER = %r{ \[RAMCELL\]\s*#{NEWLINE} }x
|
21
|
+
STRING = %r{ [^\r\n;]+ }x
|
22
|
+
WHITESPACE = %r{ [ \t]+ }x
|
23
|
+
SEPARATOR = %r{ [;] }x
|
24
|
+
COMMENT = %r{ ^[;].*$ }x
|
25
|
+
|
26
|
+
def next_token(state)
|
27
|
+
@scan.skip(WHITESPACE)
|
28
|
+
|
29
|
+
return if @scan.eos?
|
30
|
+
|
31
|
+
case
|
32
|
+
when @scan.skip(SETTINGS_HEADER) then :SETTINGS_HEADER
|
33
|
+
when @scan.skip(LABELS_HEADER) then :LABELS_HEADER
|
34
|
+
when @scan.skip("[LABEL]\n") then :LABELS_HEADER
|
35
|
+
when @scan.skip(SIGNALS_HEADER) then :SIGNALS_HEADER
|
36
|
+
when @scan.skip(STRING) then :STRING
|
37
|
+
when @scan.beginning_of_line? && @scan.skip(COMMENT) then :COMMENT
|
38
|
+
when @scan.skip(SEPARATOR) then :SEPARATOR
|
39
|
+
when @scan.skip(NEWLINE) then :NEWLINE
|
40
|
+
when @scan.skip(ANYTHING) then :ANYTHING
|
41
|
+
|
42
|
+
else
|
43
|
+
@scan.getch
|
44
|
+
:UNKNOWN_CHAR
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def token_value
|
49
|
+
@doc.byteslice(@scan.pos - @scan.matched_size, @scan.matched_size)
|
50
|
+
end
|
51
|
+
|
52
|
+
def lineno
|
53
|
+
@doc.byteslice(0, @scan.pos).count("\n") +
|
54
|
+
(@scan.beginning_of_line? ? 0 : 1)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,61 +1,132 @@
|
|
1
1
|
require_relative "../../labels"
|
2
2
|
require_relative "../../signals"
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
lines = str.lines
|
7
|
-
|
8
|
-
[
|
9
|
-
extract_signals(block_bounded_by(lines, "[RAMCELL]", /\A\[/)),
|
10
|
-
extract_labels(block_bounded_by(lines, "[Label]", /\A\[/)),
|
11
|
-
extract_headers(block_bounded_by(lines, 0, "[SETTINGS]")),
|
12
|
-
extract_headers(block_bounded_by(lines, "[SETTINGS]", /\A\[/)),
|
13
|
-
]
|
14
|
-
end
|
4
|
+
require_relative "lab_lexer"
|
5
|
+
require_relative "lab_parser_error"
|
15
6
|
|
16
|
-
|
17
|
-
|
18
|
-
reject { |l| l.blank? }.
|
19
|
-
reject { |l| l.start_with?(";") }.
|
20
|
-
map { |l| Ecu::Label.from_lab(l) }
|
21
|
-
end
|
7
|
+
class Ecu
|
8
|
+
class LabParser
|
22
9
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
10
|
+
attr_reader :lexer, :state, :labels, :signals, :headers, :subheaders
|
11
|
+
def initialize(doc)
|
12
|
+
@lexer = LabLexer.new(doc)
|
13
|
+
@state = :NO_SECTION
|
14
|
+
@signals = []
|
15
|
+
@labels = []
|
16
|
+
@headers = []
|
17
|
+
@subheaders = []
|
18
|
+
reset_entry!
|
19
|
+
end
|
29
20
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
map { |l| l[2..-1].chomp }
|
34
|
-
end
|
21
|
+
def reset_entry!
|
22
|
+
@properties = {}
|
23
|
+
end
|
35
24
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
25
|
+
def call
|
26
|
+
while tok = lexer.next_token(state)
|
27
|
+
begin
|
28
|
+
case state
|
29
|
+
in :NO_SECTION
|
30
|
+
case tok
|
31
|
+
in :SETTINGS_HEADER then next_state(:SETTINGS)
|
32
|
+
in :SIGNALS_HEADER then next_state(:SIGNALS)
|
33
|
+
in :LABELS_HEADER then next_state(:LABELS)
|
34
|
+
in :COMMENT then add_header(lexer.token_value)
|
35
|
+
in :NEWLINE then # noop
|
36
|
+
end
|
37
|
+
in :SETTINGS
|
38
|
+
case tok
|
39
|
+
in :SIGNALS_HEADER then next_state(:SIGNALS)
|
40
|
+
in :LABELS_HEADER then next_state(:LABELS)
|
41
|
+
in :COMMENT then add_subheader(lexer.token_value)
|
42
|
+
in :STRING then # save that shit?
|
43
|
+
in :SEPARATOR then # now comes the value
|
44
|
+
in :NEWLINE then # next setting
|
45
|
+
end
|
46
|
+
in :LABELS
|
47
|
+
case tok
|
48
|
+
in :SIGNALS_HEADER then next_state(:SIGNALS)
|
49
|
+
in :STRING then start_label(lexer.token_value)
|
50
|
+
in :COMMENT then # noop
|
51
|
+
in :NEWLINE then # noop
|
52
|
+
end
|
53
|
+
in :SIGNALS
|
54
|
+
case tok
|
55
|
+
in :LABELS_HEADER then next_state(:LABELS)
|
56
|
+
in :STRING then start_signal(lexer.token_value)
|
57
|
+
in :COMMENT then # noop
|
58
|
+
in :NEWLINE then # noop
|
59
|
+
end
|
60
|
+
in :LABEL
|
61
|
+
case tok
|
62
|
+
in :NEWLINE then labels << finish_label
|
63
|
+
in :SEPARATOR then # noop, sometimes used as end separator
|
45
64
|
end
|
65
|
+
in :SIGNAL
|
66
|
+
case tok
|
67
|
+
in :STRING then add_property(lexer.token_value)
|
68
|
+
in :SEPARATOR then # noop, next property
|
69
|
+
in :NEWLINE then signals << finish_signal
|
70
|
+
end
|
71
|
+
end
|
72
|
+
rescue NoMatchingPatternError => e
|
73
|
+
raise LabParserError.new("Unexpected token #{debug_token(tok)} (state: #{state})", lexer)
|
74
|
+
rescue StandardError => e
|
75
|
+
raise LabParserError.new("#{e.message} (state: #{@state})", lexer)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
[signals, labels, headers, subheaders]
|
79
|
+
end
|
46
80
|
|
47
|
-
|
81
|
+
def debug_token(tok)
|
82
|
+
"#{tok}: \"#{lexer.token_value}\" - #{@properties.inspect}"
|
83
|
+
end
|
48
84
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
85
|
+
def next_state(newstate)
|
86
|
+
@state = newstate
|
87
|
+
yield if block_given?
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_header(str)
|
91
|
+
@headers << str[1..].strip
|
92
|
+
end
|
93
|
+
|
94
|
+
def add_subheader(str)
|
95
|
+
@subheaders << str[1..].strip
|
96
|
+
end
|
97
|
+
|
98
|
+
def start_label(name)
|
99
|
+
next_state(:LABEL) do
|
100
|
+
@properties[:name] = name
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def finish_label
|
105
|
+
Festwert.new(**@properties, value: nil).tap do
|
106
|
+
reset_entry!
|
107
|
+
next_state(:LABELS)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def start_signal(name)
|
112
|
+
next_state(:SIGNAL) do
|
113
|
+
@properties[:name] = name
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def finish_signal
|
118
|
+
Signal.new(**@properties).tap do
|
119
|
+
reset_entry!
|
120
|
+
next_state(:SIGNALS)
|
121
|
+
end
|
122
|
+
end
|
58
123
|
|
59
|
-
|
124
|
+
def add_property(str)
|
125
|
+
case @properties
|
126
|
+
in { task:, description: } then @properties[:description] << " " << str
|
127
|
+
in { task: } then @properties[:description] = str.strip
|
128
|
+
in { ** } then @properties[:task] = str.split("&").first
|
129
|
+
end
|
130
|
+
end
|
60
131
|
end
|
61
132
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# TODO: This is exactly the same as the DcmParserError, maybe pull these
|
2
|
+
# together?
|
3
|
+
|
4
|
+
class Ecu
|
5
|
+
class LabParserError < StandardError
|
6
|
+
|
7
|
+
CTXLENGHT = 5
|
8
|
+
|
9
|
+
attr_reader :doc, :lineno
|
10
|
+
def initialize(msg, lexer)
|
11
|
+
@msg = msg
|
12
|
+
@doc = lexer.doc
|
13
|
+
@lineno = lexer.lineno
|
14
|
+
end
|
15
|
+
|
16
|
+
def message = @msg
|
17
|
+
def context
|
18
|
+
@doc
|
19
|
+
.lines[ctx_startline..ctx_endline]
|
20
|
+
.each
|
21
|
+
.with_index(ctx_startline + 1)
|
22
|
+
.map { |line, n| present(line, n, n == lineno) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def present(line, n, highlight)
|
26
|
+
case highlight
|
27
|
+
when true then fmt(n) + " => | " + line
|
28
|
+
when false then fmt(n) + " | " + line
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def ctx_startline = [0, lineno - CTXLENGHT].max
|
33
|
+
def ctx_endline = @endline ||= [doc.lines.count - 1, lineno + CTXLENGHT].min
|
34
|
+
def fmt_str = "%#{(ctx_endline + 1).to_s.length}d"
|
35
|
+
def fmt(n) = fmt_str % n
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -1,19 +1,5 @@
|
|
1
1
|
class Ecu
|
2
2
|
class Label
|
3
|
-
|
4
|
-
# This is a silly hack, since the *.lab file doesn't contain any information
|
5
|
-
# about the label in question (just the name). Subsequently, just initialize
|
6
|
-
# a Festwert with no value and hope if does the job correctly
|
7
|
-
|
8
|
-
def self.from_lab(line)
|
9
|
-
name, description = line.strip.split(";")
|
10
|
-
if description.nil?
|
11
|
-
Festwert.new(name: name, value: nil)
|
12
|
-
else
|
13
|
-
Festwert.new(name: name, value: nil, description: description)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
3
|
def to_lab
|
18
4
|
"#{name};#{description}"
|
19
5
|
end
|
@@ -1,13 +1,5 @@
|
|
1
1
|
class Ecu
|
2
2
|
class Signal
|
3
|
-
def self.from_lab(line)
|
4
|
-
name, tasks, comment = line.strip.split(";")
|
5
|
-
maintask = tasks.split("&").first rescue nil
|
6
|
-
new(name: name,
|
7
|
-
task: maintask,
|
8
|
-
description: comment&.strip)
|
9
|
-
end
|
10
|
-
|
11
3
|
def to_lab
|
12
4
|
"#{name};#{task};#{description}"
|
13
5
|
end
|
data/lib/ecu/labels/label.rb
CHANGED
@@ -13,7 +13,15 @@ class Ecu
|
|
13
13
|
attr_reader :name, :function, :description
|
14
14
|
|
15
15
|
def init_error(message)
|
16
|
-
fail ArgumentError, "Error creating #{name}: " +
|
16
|
+
fail ArgumentError, "Error creating #{name}: " +
|
17
|
+
message + "\n" +
|
18
|
+
inspect_instance_vars + "\n\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect_instance_vars
|
22
|
+
instance_variables
|
23
|
+
.map { "#{_1} = #{instance_variable_get(_1)}" }
|
24
|
+
.join("\n")
|
17
25
|
end
|
18
26
|
|
19
27
|
def type
|
@@ -10,6 +10,7 @@ require_relative "../interfaces/dcm/label_list"
|
|
10
10
|
require_relative "../interfaces/lab/label_list"
|
11
11
|
require_relative "../interfaces/a2l/label_list"
|
12
12
|
require_relative "../interfaces/mfile/label_list"
|
13
|
+
require_relative "../interfaces/json/label_list"
|
13
14
|
|
14
15
|
require "set"
|
15
16
|
require_relative "../../core_ext"
|
data/lib/ecu/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: automotive-ecu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Mueller
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-04-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: terminal-table
|
@@ -97,6 +97,7 @@ files:
|
|
97
97
|
- bin/pry
|
98
98
|
- bin/rake
|
99
99
|
- bin/rspec
|
100
|
+
- bin/sandbox
|
100
101
|
- bin/setup
|
101
102
|
- lib/core_ext.rb
|
102
103
|
- lib/ecu.rb
|
@@ -105,23 +106,25 @@ files:
|
|
105
106
|
- lib/ecu/interfaces/a2l/label_list.rb
|
106
107
|
- lib/ecu/interfaces/a2l/signal_list.rb
|
107
108
|
- lib/ecu/interfaces/dbc/signal_list.rb
|
108
|
-
- lib/ecu/interfaces/dcm/
|
109
|
+
- lib/ecu/interfaces/dcm/dcm_lexer.rb
|
110
|
+
- lib/ecu/interfaces/dcm/dcm_parser.rb
|
111
|
+
- lib/ecu/interfaces/dcm/dcm_parser_error.rb
|
109
112
|
- lib/ecu/interfaces/dcm/festkennfeld.rb
|
110
113
|
- lib/ecu/interfaces/dcm/festkennlinie.rb
|
111
114
|
- lib/ecu/interfaces/dcm/festwert.rb
|
112
115
|
- lib/ecu/interfaces/dcm/festwerteblock.rb
|
113
|
-
- lib/ecu/interfaces/dcm/functions.rb
|
114
116
|
- lib/ecu/interfaces/dcm/gruppenkennfeld.rb
|
115
117
|
- lib/ecu/interfaces/dcm/gruppenkennlinie.rb
|
116
118
|
- lib/ecu/interfaces/dcm/kennfeld.rb
|
117
119
|
- lib/ecu/interfaces/dcm/kennlinie.rb
|
118
120
|
- lib/ecu/interfaces/dcm/label.rb
|
119
121
|
- lib/ecu/interfaces/dcm/label_list.rb
|
120
|
-
- lib/ecu/interfaces/dcm/malformed_dcm_error.rb
|
121
|
-
- lib/ecu/interfaces/dcm/property_parser.rb
|
122
122
|
- lib/ecu/interfaces/dcm/stuetzstellenverteilung.rb
|
123
|
+
- lib/ecu/interfaces/json/label_list.rb
|
123
124
|
- lib/ecu/interfaces/lab/combined_list.rb
|
125
|
+
- lib/ecu/interfaces/lab/lab_lexer.rb
|
124
126
|
- lib/ecu/interfaces/lab/lab_parser.rb
|
127
|
+
- lib/ecu/interfaces/lab/lab_parser_error.rb
|
125
128
|
- lib/ecu/interfaces/lab/label.rb
|
126
129
|
- lib/ecu/interfaces/lab/label_list.rb
|
127
130
|
- lib/ecu/interfaces/lab/signal.rb
|
@@ -1,39 +0,0 @@
|
|
1
|
-
class DcmBuffer
|
2
|
-
attr_reader :buffer, :header, :constructor
|
3
|
-
def initialize
|
4
|
-
@constructor = nil
|
5
|
-
@header = :pre
|
6
|
-
@buffer = []
|
7
|
-
end
|
8
|
-
|
9
|
-
def start!(constructor, buffer)
|
10
|
-
fail "Nested parameter" unless self.buffer.empty?
|
11
|
-
fail "Missing DCM header" if self.header == :pre
|
12
|
-
|
13
|
-
@header = :done
|
14
|
-
@constructor = constructor
|
15
|
-
@buffer = buffer
|
16
|
-
end
|
17
|
-
|
18
|
-
def finish!(line)
|
19
|
-
fail "Unexpected END" if constructor.nil?
|
20
|
-
append!(line)
|
21
|
-
|
22
|
-
constructor.from_dcm(buffer).tap { reset! }
|
23
|
-
end
|
24
|
-
|
25
|
-
def reset!
|
26
|
-
@constructor = nil
|
27
|
-
@buffer = []
|
28
|
-
end
|
29
|
-
|
30
|
-
def header_seen!
|
31
|
-
@header = :after
|
32
|
-
end
|
33
|
-
|
34
|
-
def append!(line)
|
35
|
-
fail "No label started" if constructor.nil?
|
36
|
-
|
37
|
-
@buffer << line
|
38
|
-
end
|
39
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
class Ecu
|
2
|
-
class MalformedDcmError < StandardError
|
3
|
-
attr_reader :lineno, :filecontent
|
4
|
-
def initialize(msg, lineno, filecontent)
|
5
|
-
super(msg)
|
6
|
-
@lineno = lineno
|
7
|
-
@filecontent = filecontent
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_s = "Malformed DCM: #{super} near line ##{lineno}"
|
11
|
-
def to_str = to_s
|
12
|
-
|
13
|
-
def context
|
14
|
-
filecontent
|
15
|
-
.lines
|
16
|
-
.each
|
17
|
-
.with_index(1)
|
18
|
-
.map { |line, n| present(line, n, n == lineno) }
|
19
|
-
.then { _1[ctx_startline..ctx_endline] }
|
20
|
-
end
|
21
|
-
|
22
|
-
def present(line, n, highlight)
|
23
|
-
case highlight
|
24
|
-
when true then fmt(n) + " => | " + line
|
25
|
-
when false then fmt(n) + " | " + line
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def ctx_startline = [0, lineno - 7].max
|
30
|
-
def ctx_endline = @endline ||= [filecontent.lines.count - 1, lineno + 3].min
|
31
|
-
def fmt_str = "%#{(ctx_endline + 1).to_s.length}d"
|
32
|
-
def fmt(n) = fmt_str % n
|
33
|
-
end
|
34
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
class Ecu
|
2
|
-
class DcmPropertyParser
|
3
|
-
|
4
|
-
def self.call(line)
|
5
|
-
key, _, value = line.strip.partition(/\s+/)
|
6
|
-
eval_property(key, value)
|
7
|
-
end
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
def self.eval_property(key, value)
|
12
|
-
case key
|
13
|
-
when "ST/X" then { xvalue: numeric_array(value) }
|
14
|
-
when "ST/Y" then { yvalue: numeric_array(value) }
|
15
|
-
when "WERT" then { value: numeric_array(value) }
|
16
|
-
when "ST_TX/X" then { xvalue: string_array(value) }
|
17
|
-
when "ST_TX/Y" then { yvalue: string_array(value) }
|
18
|
-
when "TEXT" then { value: string_array(value) }
|
19
|
-
when "FUNKTION" then { function: value }
|
20
|
-
when "EINHEIT_X" then { xunit: string_value(value) }
|
21
|
-
when "EINHEIT_Y" then { yunit: string_value(value) }
|
22
|
-
when "EINHEIT_W" then { unit: string_value(value) }
|
23
|
-
when "LANGNAME" then { description: string_value(value) }
|
24
|
-
when "DISPLAYNAME" then { }
|
25
|
-
else fail ArgumentError, "Unknown key #{key}"
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.string_value(str)
|
30
|
-
str.delete_surrounding('"')
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.numeric_array(str)
|
34
|
-
str.split.map { numeric_value(_1) }
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.string_array(str)
|
38
|
-
str.scan(/"([^"]*)"/).flatten
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.numeric_value(str)
|
42
|
-
case str
|
43
|
-
in /^\d+$/ then str.to_i
|
44
|
-
in /^\d+\.$/ then str.to_f
|
45
|
-
else Float(str)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
end
|