automotive-ecu 0.1.9 → 0.2.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/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 +6 -58
- data/lib/ecu/interfaces/dcm/stuetzstellenverteilung.rb +0 -4
- data/lib/ecu/labels/label.rb +9 -1
- data/lib/ecu/version.rb +1 -1
- metadata +19 -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: d05b1f41faab9eb4a9ffb33697b015f0242d64450f25bedf6ce47aef2aac08ca
|
4
|
+
data.tar.gz: ecdc24755f8244e51a2bc20ee9efef50f3e40be5f6a2d5f678d5bdea462f5c8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1b92c416a9dacb5dd9c28afd30bdf9de68e31390977612752bf23365cf9a9b93894d7e4641bb5629832debb237d549fb4f38c475b2b55ad812c271b99b193e52
|
7
|
+
data.tar.gz: f8548dd035ac77f453d2b36717f398107d16207febfb886bc26964a6c84ef1582b0e41ea1039fed4b3afc0ebd48a8230e21eb47255d1064c71ae6f5ab90c9e74
|
@@ -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
|
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 :"ST/X" then start_property(:"ST/X", :NUMERIC_PROPERTY)
|
81
|
+
in :"ST/Y" then start_property(:"ST/Y", :NUMERIC_PROPERTY)
|
82
|
+
in :WERT then start_property(:WERT, :NUMERIC_PROPERTY)
|
83
|
+
in :"ST_TX/X" then start_property(:"ST_TX/X", :TEXT_PROPERTY)
|
84
|
+
in :"ST_TX/Y" then start_property(:"ST_TX/Y", :TEXT_PROPERTY)
|
85
|
+
in :TEXT then start_property(:TEXT, :TEXT_PROPERTY)
|
86
|
+
in :EINHEIT_X then start_property(:EINHEIT_X, :TEXT_PROPERTY)
|
87
|
+
in :EINHEIT_Y then start_property(:EINHEIT_Y, :TEXT_PROPERTY)
|
88
|
+
in :EINHEIT_W then start_property(:EINHEIT_W, :TEXT_PROPERTY)
|
89
|
+
in :LANGNAME then start_property(:LANGNAME, :TEXT_PROPERTY)
|
90
|
+
in :FUNKTION then start_property(:FUNKTION, :ID_PROPERTY)
|
91
|
+
in :DISPLAYNAME then start_property(:DISPLAYNAME, :ID_PROPERTY)
|
92
|
+
in :END then labels << finish_label
|
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,18 @@ 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)
|
70
|
-
end
|
71
|
-
|
72
|
-
def self.normalize_whitespace(line)
|
73
|
-
line.chomp.gsub(/[[:space:]]/, " ").rstrip
|
19
|
+
DcmParser.new(str)
|
20
|
+
.then { _1.call }
|
21
|
+
.then { new(*_1, true) }
|
74
22
|
end
|
75
23
|
|
76
24
|
def to_dcm(indented=false)
|
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
|
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.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Mueller
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: terminal-table
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3.10'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: benchmark-ips
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
description: Provides an interface for exporting/importing signals and labels to various
|
70
84
|
automotive formats (*.lab, *.dcm)
|
71
85
|
email:
|
@@ -91,20 +105,19 @@ files:
|
|
91
105
|
- lib/ecu/interfaces/a2l/label_list.rb
|
92
106
|
- lib/ecu/interfaces/a2l/signal_list.rb
|
93
107
|
- lib/ecu/interfaces/dbc/signal_list.rb
|
94
|
-
- lib/ecu/interfaces/dcm/
|
108
|
+
- lib/ecu/interfaces/dcm/dcm_lexer.rb
|
109
|
+
- lib/ecu/interfaces/dcm/dcm_parser.rb
|
110
|
+
- lib/ecu/interfaces/dcm/dcm_parser_error.rb
|
95
111
|
- lib/ecu/interfaces/dcm/festkennfeld.rb
|
96
112
|
- lib/ecu/interfaces/dcm/festkennlinie.rb
|
97
113
|
- lib/ecu/interfaces/dcm/festwert.rb
|
98
114
|
- lib/ecu/interfaces/dcm/festwerteblock.rb
|
99
|
-
- lib/ecu/interfaces/dcm/functions.rb
|
100
115
|
- lib/ecu/interfaces/dcm/gruppenkennfeld.rb
|
101
116
|
- lib/ecu/interfaces/dcm/gruppenkennlinie.rb
|
102
117
|
- lib/ecu/interfaces/dcm/kennfeld.rb
|
103
118
|
- lib/ecu/interfaces/dcm/kennlinie.rb
|
104
119
|
- lib/ecu/interfaces/dcm/label.rb
|
105
120
|
- lib/ecu/interfaces/dcm/label_list.rb
|
106
|
-
- lib/ecu/interfaces/dcm/malformed_dcm_error.rb
|
107
|
-
- lib/ecu/interfaces/dcm/property_parser.rb
|
108
121
|
- lib/ecu/interfaces/dcm/stuetzstellenverteilung.rb
|
109
122
|
- lib/ecu/interfaces/lab/combined_list.rb
|
110
123
|
- lib/ecu/interfaces/lab/lab_parser.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
|