automotive-ecu 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/bundle +114 -0
- data/bin/coderay +29 -0
- data/bin/console +14 -0
- data/bin/htmldiff +29 -0
- data/bin/irb +29 -0
- data/bin/ldiff +29 -0
- data/bin/pry +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/setup +8 -0
- data/lib/core_ext.rb +69 -0
- data/lib/ecu/combined_list.rb +25 -0
- data/lib/ecu/combined_list_comparison.rb +50 -0
- data/lib/ecu/interfaces/a2l/label_list.rb +18 -0
- data/lib/ecu/interfaces/a2l/signal_list.rb +15 -0
- data/lib/ecu/interfaces/dbc/signal_list.rb +12 -0
- data/lib/ecu/interfaces/dcm/buffer.rb +39 -0
- data/lib/ecu/interfaces/dcm/festkennfeld.rb +9 -0
- data/lib/ecu/interfaces/dcm/festkennlinie.rb +9 -0
- data/lib/ecu/interfaces/dcm/festwert.rb +20 -0
- data/lib/ecu/interfaces/dcm/festwerteblock.rb +26 -0
- data/lib/ecu/interfaces/dcm/functions.rb +13 -0
- data/lib/ecu/interfaces/dcm/gruppenkennfeld.rb +9 -0
- data/lib/ecu/interfaces/dcm/gruppenkennlinie.rb +9 -0
- data/lib/ecu/interfaces/dcm/kennfeld.rb +32 -0
- data/lib/ecu/interfaces/dcm/kennlinie.rb +25 -0
- data/lib/ecu/interfaces/dcm/label.rb +25 -0
- data/lib/ecu/interfaces/dcm/label_list.rb +94 -0
- data/lib/ecu/interfaces/dcm/malformed_dcm_error.rb +34 -0
- data/lib/ecu/interfaces/dcm/property_parser.rb +51 -0
- data/lib/ecu/interfaces/dcm/stuetzstellenverteilung.rb +20 -0
- data/lib/ecu/interfaces/lab/combined_list.rb +32 -0
- data/lib/ecu/interfaces/lab/lab_parser.rb +61 -0
- data/lib/ecu/interfaces/lab/label.rb +21 -0
- data/lib/ecu/interfaces/lab/label_list.rb +29 -0
- data/lib/ecu/interfaces/lab/signal.rb +15 -0
- data/lib/ecu/interfaces/lab/signal_list.rb +29 -0
- data/lib/ecu/interfaces/mfile/festwert.rb +10 -0
- data/lib/ecu/interfaces/mfile/festwerteblock.rb +12 -0
- data/lib/ecu/interfaces/mfile/kennfeld.rb +12 -0
- data/lib/ecu/interfaces/mfile/kennlinie.rb +7 -0
- data/lib/ecu/interfaces/mfile/label_list.rb +13 -0
- data/lib/ecu/interfaces/mfile/stuetzstellenverteilung.rb +7 -0
- data/lib/ecu/labels/festwert.rb +58 -0
- data/lib/ecu/labels/festwerteblock.rb +80 -0
- data/lib/ecu/labels/interpolator.rb +55 -0
- data/lib/ecu/labels/kennfeld.rb +107 -0
- data/lib/ecu/labels/kennlinie.rb +84 -0
- data/lib/ecu/labels/label.rb +82 -0
- data/lib/ecu/labels/label_list.rb +137 -0
- data/lib/ecu/labels/label_list_comparison.rb +65 -0
- data/lib/ecu/labels/stuetzstellenverteilung.rb +65 -0
- data/lib/ecu/labels/value_comparison.rb +37 -0
- data/lib/ecu/labels/value_printer.rb +49 -0
- data/lib/ecu/labels.rb +2 -0
- data/lib/ecu/signals/signal.rb +73 -0
- data/lib/ecu/signals/signal_list.rb +68 -0
- data/lib/ecu/signals/signal_list_comparison.rb +52 -0
- data/lib/ecu/signals.rb +2 -0
- data/lib/ecu/version.rb +3 -0
- data/lib/ecu.rb +59 -0
- metadata +161 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
require_relative "label"
|
2
|
+
|
3
|
+
class Ecu
|
4
|
+
class Festwerteblock < Label
|
5
|
+
|
6
|
+
attr_reader :xdim, :ydim, :value, :unit
|
7
|
+
|
8
|
+
def initialize(name:,
|
9
|
+
xdim:,
|
10
|
+
ydim: 1,
|
11
|
+
value:,
|
12
|
+
unit: nil,
|
13
|
+
function: nil,
|
14
|
+
description: nil)
|
15
|
+
value = reshape_if_needed(value, xdim, ydim)
|
16
|
+
ydim = ydim == 0 ? 1 : ydim
|
17
|
+
@name = name
|
18
|
+
@xdim = xdim
|
19
|
+
@ydim = ydim
|
20
|
+
@value = value
|
21
|
+
@unit = unit
|
22
|
+
@function = function
|
23
|
+
@description = description
|
24
|
+
|
25
|
+
init_error "Dimensions for value don't match ydim" if value.size != ydim
|
26
|
+
init_error "Dimensions for value don't match xdim" if value.any? { |row| row.is_a?(Array) && row.size != xdim }
|
27
|
+
end
|
28
|
+
|
29
|
+
def round_to(n)
|
30
|
+
return self if value.any? { |row| row.any? { |e| e.is_a?(String) }}
|
31
|
+
self.with \
|
32
|
+
value: value.map { |row| row.map { |x| x.round(n) }}
|
33
|
+
end
|
34
|
+
|
35
|
+
def bytesize
|
36
|
+
xdim * ydim * BYTESIZE[:number]
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s(detail: false)
|
40
|
+
if detail == :value
|
41
|
+
"#{name}:\n#{to_s(detail: :justvalue)}"
|
42
|
+
elsif detail == :justvalue
|
43
|
+
ValuePrinter.call(self)
|
44
|
+
elsif detail == :onelinefull
|
45
|
+
"#{name} #{to_s(detail: :oneline)}"
|
46
|
+
elsif detail == :oneline
|
47
|
+
"(#{xdim}x#{ydim}) Z: #{valuestats(value, show_avg: true)}"
|
48
|
+
else
|
49
|
+
"#{name}: #{xdim}x#{ydim} #{type}".tap do |str|
|
50
|
+
if detail
|
51
|
+
str << "\n"
|
52
|
+
str << " Unit: \"#{unit}\"\n"
|
53
|
+
str << " Value:\n"
|
54
|
+
str << ValuePrinter.call(self).indent(4)
|
55
|
+
str << "\n"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def properties
|
62
|
+
[:name, :xdim, :ydim, :value, :unit, :function, :description]
|
63
|
+
end
|
64
|
+
|
65
|
+
def equality_properties
|
66
|
+
[:name, :value]
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def reshape_if_needed(value, xdim, ydim)
|
72
|
+
if value.all? { |row| row.is_a?(Array) }
|
73
|
+
value
|
74
|
+
else
|
75
|
+
value.each_slice(xdim).to_a
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class Array
|
2
|
+
def strictly_mon_inc?
|
3
|
+
self.each_cons(2).all? { |lo, hi| lo < hi }
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class Ecu
|
8
|
+
class Interpolator
|
9
|
+
def self.interp1(x_ref, y_ref, x)
|
10
|
+
raise ArgumentError unless x_ref.size == y_ref.size
|
11
|
+
raise ArgumentError unless x_ref.strictly_mon_inc?
|
12
|
+
|
13
|
+
return y_ref.first if x < x_ref.first
|
14
|
+
return y_ref.last if x > x_ref.last
|
15
|
+
|
16
|
+
x1, x2, fx = find_position(x_ref, x)
|
17
|
+
|
18
|
+
interp(y_ref[x1], y_ref[x2], fx)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.interp2(x_ref, y_ref, z_ref, x, y)
|
22
|
+
raise ArgumentError unless z_ref.size == y_ref.size
|
23
|
+
raise ArgumentError unless z_ref.all? { |row| row.size == x_ref.size }
|
24
|
+
raise ArgumentError unless x_ref.strictly_mon_inc?
|
25
|
+
raise ArgumentError unless y_ref.strictly_mon_inc?
|
26
|
+
|
27
|
+
x1, x2, fx = find_position(x_ref, x)
|
28
|
+
y1, y2, fy = find_position(y_ref, y)
|
29
|
+
|
30
|
+
interp(interp(z_ref[y1][x1], z_ref[y2][x1], fy),
|
31
|
+
interp(z_ref[y1][x2], z_ref[y2][x2], fy),
|
32
|
+
fx)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def self.find_position(ref, x)
|
38
|
+
x = x.to_f
|
39
|
+
n = ref.size - 1
|
40
|
+
return [0, 0, 0] if x < ref.first
|
41
|
+
return [n, n, 0] if x > ref.last
|
42
|
+
|
43
|
+
ref.each_cons(2).each do |lo, hi|
|
44
|
+
if (lo..hi).cover?(x)
|
45
|
+
f = (x - lo)/(hi - lo)
|
46
|
+
return [ref.index(lo), ref.index(hi), f]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.interp(x, y, f)
|
52
|
+
x*(1-f) + y*f
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require_relative "label"
|
2
|
+
require_relative "interpolator"
|
3
|
+
|
4
|
+
class Ecu
|
5
|
+
class Kennfeld < Label
|
6
|
+
|
7
|
+
attr_reader :value, :unit
|
8
|
+
attr_reader :xdim, :xvalue, :xunit
|
9
|
+
attr_reader :ydim, :yvalue, :yunit
|
10
|
+
|
11
|
+
def initialize(name:,
|
12
|
+
xdim:,
|
13
|
+
ydim:,
|
14
|
+
xvalue:,
|
15
|
+
yvalue:,
|
16
|
+
value:,
|
17
|
+
xunit: nil,
|
18
|
+
yunit: nil,
|
19
|
+
unit: nil,
|
20
|
+
function: nil,
|
21
|
+
description: nil)
|
22
|
+
value = value.size > ydim ? value.each_slice(xdim).to_a : value
|
23
|
+
@name = name
|
24
|
+
@xdim = xdim
|
25
|
+
@ydim = ydim
|
26
|
+
@xvalue = xvalue
|
27
|
+
@yvalue = yvalue
|
28
|
+
@value = value
|
29
|
+
@xunit = xunit
|
30
|
+
@yunit = yunit
|
31
|
+
@unit = unit
|
32
|
+
@function = function
|
33
|
+
@description = description
|
34
|
+
|
35
|
+
init_error "Dimension mismatch: xvalue=#{xvalue.size} xdim=#{xdim}" if xvalue.size != xdim
|
36
|
+
init_error "Dimension mismatch: yvalue=#{yvalue.size} ydim=#{ydim}" if yvalue.size != ydim
|
37
|
+
init_error "Dimension mismatch: value.x=#{value.size} ydim=#{ydim}" if value.size != ydim
|
38
|
+
init_error "Dimension mismatch: value.y=XXX xdim=#{xdim}" if value.any? { |r| r.size != xdim }
|
39
|
+
end
|
40
|
+
|
41
|
+
def value_at(x, y)
|
42
|
+
Interpolator.interp2(xvalue, yvalue, value, x, y)
|
43
|
+
end
|
44
|
+
|
45
|
+
def round_to(n)
|
46
|
+
hsh = Hash.new
|
47
|
+
hsh[:xvalue] = xvalue.map { |x| x.round(n) } if xvalue.all? { |x| x.is_a?(Numeric) }
|
48
|
+
hsh[:yvalue] = yvalue.map { |x| x.round(n) } if yvalue.all? { |x| x.is_a?(Numeric) }
|
49
|
+
if value.all? { |row| row.all? { |x| x.is_a?(Numeric) }}
|
50
|
+
hsh[:value] = value.map { |row| row.map { |x| x.round(n) }}
|
51
|
+
end
|
52
|
+
self.with hsh
|
53
|
+
end
|
54
|
+
|
55
|
+
def reinterpx(new_xvalue)
|
56
|
+
self.with \
|
57
|
+
xvalue: new_xvalue,
|
58
|
+
value: yvalue.map { |y| new_xvalue.map { |x| value_at(x, y) } }
|
59
|
+
end
|
60
|
+
|
61
|
+
def reinterpy(new_yvalue)
|
62
|
+
self.with \
|
63
|
+
yvalue: new_yvalue,
|
64
|
+
value: new_yvalue.map { |y| xvalue.map { |x| value_at(x, y) } }
|
65
|
+
end
|
66
|
+
|
67
|
+
def bytesize
|
68
|
+
(xdim + 1) * (ydim + 1) * BYTESIZE[:number]
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s(detail: false)
|
72
|
+
if detail == :value
|
73
|
+
"#{name}:\n#{to_s(detail: :justvalue)}"
|
74
|
+
elsif detail == :justvalue
|
75
|
+
ValuePrinter.call(self)
|
76
|
+
elsif detail == :onelinefull
|
77
|
+
"#{name} #{to_s(detail: :oneline)}"
|
78
|
+
elsif detail == :oneline
|
79
|
+
"(#{xdim}x#{ydim}) " +
|
80
|
+
"X: #{valuestats(xvalue)}, " +
|
81
|
+
"Y: #{valuestats(yvalue)}, " +
|
82
|
+
"Z: #{valuestats(value, show_avg: true)}"
|
83
|
+
else
|
84
|
+
"#{name}: #{@xdim}x#{@ydim} #{type}".tap do |str|
|
85
|
+
if detail
|
86
|
+
str << "\n"
|
87
|
+
str << " x-Unit: \"#{xunit}\"\n"
|
88
|
+
str << " y-Unit: \"#{yunit}\"\n"
|
89
|
+
str << " z-Unit: \"#{unit}\"\n"
|
90
|
+
str << " Value:\n"
|
91
|
+
str << ValuePrinter.call(self).indent(4)
|
92
|
+
str << "\n"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def properties
|
99
|
+
[:name, :xdim, :ydim, :xvalue, :yvalue, :value, :xunit, :yunit, :unit, :function, :description]
|
100
|
+
end
|
101
|
+
|
102
|
+
def equality_properties
|
103
|
+
[:name, :xvalue, :yvalue, :value]
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require_relative "label"
|
2
|
+
require_relative "interpolator"
|
3
|
+
|
4
|
+
class Ecu
|
5
|
+
class Kennlinie < Label
|
6
|
+
|
7
|
+
attr_reader :xdim, :xvalue, :xunit
|
8
|
+
attr_reader :value, :unit
|
9
|
+
|
10
|
+
def initialize(name:,
|
11
|
+
xdim:,
|
12
|
+
xvalue:,
|
13
|
+
value:,
|
14
|
+
xunit: nil,
|
15
|
+
unit: nil,
|
16
|
+
function: nil,
|
17
|
+
description: nil)
|
18
|
+
@name = name
|
19
|
+
@xdim = xdim
|
20
|
+
@xvalue = xvalue
|
21
|
+
@value = value
|
22
|
+
@xunit = xunit
|
23
|
+
@unit = unit
|
24
|
+
@function = function
|
25
|
+
@description = description
|
26
|
+
|
27
|
+
init_error "Dimensions for value don't match xdim" if value.size != xdim
|
28
|
+
init_error "Dimensions for xvalue don't match xdim" if xvalue.size != xdim
|
29
|
+
end
|
30
|
+
|
31
|
+
def value_at(x)
|
32
|
+
Interpolator.interp1(xvalue, value, x)
|
33
|
+
end
|
34
|
+
|
35
|
+
def round_to(n)
|
36
|
+
hsh = Hash.new
|
37
|
+
hsh[:xvalue] = xvalue.map { |x| x.round(n) } if xvalue.all? { |x| x.is_a?(Numeric) }
|
38
|
+
hsh[:value] = value.map { |x| x.round(n) } if value.all? { |x| x.is_a?(Numeric) }
|
39
|
+
self.with hsh
|
40
|
+
end
|
41
|
+
|
42
|
+
def reinterp(new_xvalue)
|
43
|
+
self.with \
|
44
|
+
xvalue: new_xvalue,
|
45
|
+
value: new_xvalue.map { |x| value_at(x) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def bytesize
|
49
|
+
(1 + 1) * (xdim) * BYTESIZE[:number]
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s(detail: false)
|
53
|
+
if detail == :value
|
54
|
+
"#{name}:\n#{to_s(detail: :justvalue)}"
|
55
|
+
elsif detail == :justvalue
|
56
|
+
ValuePrinter.call(self)
|
57
|
+
elsif detail == :onelinefull
|
58
|
+
"#{name} #{to_s(detail: :oneline)}"
|
59
|
+
elsif detail == :oneline
|
60
|
+
"(#{xdim}x1) X: #{valuestats(xvalue)}, Z: #{valuestats(value, show_avg: true)}"
|
61
|
+
else
|
62
|
+
"#{name}: #{@xdim}x1 #{type}".tap do |str|
|
63
|
+
if detail
|
64
|
+
str << "\n"
|
65
|
+
str << " x-Unit: \"#{xunit}\"\n"
|
66
|
+
str << " y-Unit: \"#{unit}\"\n"
|
67
|
+
str << " Value:\n"
|
68
|
+
str << ValuePrinter.call(self).indent(4)
|
69
|
+
str << "\n"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def properties
|
76
|
+
[:name, :xdim, :xvalue, :value, :xunit, :unit, :function, :description]
|
77
|
+
end
|
78
|
+
|
79
|
+
def equality_properties
|
80
|
+
[:name, :xvalue, :value, :type]
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require_relative "../interfaces/dcm/label"
|
2
|
+
require_relative "../interfaces/lab/label"
|
3
|
+
|
4
|
+
require_relative "value_comparison"
|
5
|
+
require_relative "value_printer"
|
6
|
+
|
7
|
+
class Ecu
|
8
|
+
class Label
|
9
|
+
|
10
|
+
BYTESIZE = { number: 4,
|
11
|
+
string: 25 }
|
12
|
+
|
13
|
+
attr_reader :name, :function, :description
|
14
|
+
|
15
|
+
def init_error(message)
|
16
|
+
fail ArgumentError, "Error creating #{name}: " + message
|
17
|
+
end
|
18
|
+
|
19
|
+
def type
|
20
|
+
self.class.name.split('::').last
|
21
|
+
end
|
22
|
+
|
23
|
+
def equality_properties
|
24
|
+
fail NotImplementedError, "Must be implemented in child classes"
|
25
|
+
end
|
26
|
+
|
27
|
+
def properties
|
28
|
+
fail NotImplementedError, "Must be implemented in child classes"
|
29
|
+
end
|
30
|
+
|
31
|
+
def <=>(other)
|
32
|
+
name <=> other.name if other.respond_to?(:name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def hash
|
36
|
+
equality_properties.map { |p| self.public_send(p) }.hash
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_h
|
40
|
+
properties.zip(properties.map { |p| instance_variable_get("@#{p}".to_sym) }).to_h
|
41
|
+
end
|
42
|
+
|
43
|
+
def with(hash={})
|
44
|
+
return self if hash.empty?
|
45
|
+
self.class.new(**to_h.merge(hash))
|
46
|
+
end
|
47
|
+
|
48
|
+
def ==(other)
|
49
|
+
return false unless other.instance_of?(self.class)
|
50
|
+
equality_properties.all? do |p|
|
51
|
+
if %i(value xvalue yvalue).include?(p)
|
52
|
+
ValueComparison.new(self.public_send(p), other.public_send(p)).eql?
|
53
|
+
else
|
54
|
+
self.public_send(p) == other.public_send(p)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def ===(other)
|
60
|
+
name == other.name
|
61
|
+
end
|
62
|
+
|
63
|
+
def match(selector)
|
64
|
+
%i(name function description).any? do |property|
|
65
|
+
self.public_send(property) && self.public_send(property).match(selector)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def valuestats(value, show_avg: false)
|
70
|
+
value = [value].flatten
|
71
|
+
min, avg, max = value.min, value.avg.round(1), value.max
|
72
|
+
if show_avg
|
73
|
+
"[#{min};~#{avg};#{max}]"
|
74
|
+
else
|
75
|
+
"[#{min};#{max}]"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
alias :eql? :==
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require_relative "festwert"
|
2
|
+
require_relative "festwerteblock"
|
3
|
+
require_relative "kennlinie"
|
4
|
+
require_relative "kennfeld"
|
5
|
+
require_relative "stuetzstellenverteilung"
|
6
|
+
|
7
|
+
require_relative "label_list_comparison"
|
8
|
+
|
9
|
+
require_relative "../interfaces/dcm/label_list"
|
10
|
+
require_relative "../interfaces/lab/label_list"
|
11
|
+
require_relative "../interfaces/a2l/label_list"
|
12
|
+
require_relative "../interfaces/mfile/label_list"
|
13
|
+
|
14
|
+
require "set"
|
15
|
+
require_relative "../../core_ext"
|
16
|
+
|
17
|
+
class Ecu
|
18
|
+
class LabelList
|
19
|
+
include Enumerable
|
20
|
+
|
21
|
+
def self.from_file(file_path)
|
22
|
+
case File.extname(file_path)
|
23
|
+
when ".lab" then self.from_lab(File.read_encoded(file_path))
|
24
|
+
when ".a2l" then self.from_a2l(File.read_encoded(file_path))
|
25
|
+
else fail "Unknown file extension: #{file_path}!"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.labelclasses
|
30
|
+
[
|
31
|
+
Festwert,
|
32
|
+
Festwerteblock,
|
33
|
+
Kennlinie,
|
34
|
+
Kennfeld,
|
35
|
+
Stuetzstellenverteilung
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(labels=[], headers=[], subheaders=[], safe=false)
|
40
|
+
unless safe
|
41
|
+
names = labels.map(&:name)
|
42
|
+
if names.uniq.size != names.size
|
43
|
+
duplicates = names.select { |name| names.count(name) > 1 }.uniq
|
44
|
+
fail ArgumentError,
|
45
|
+
"Label list must not contain duplicates: #{duplicates.join(", ")}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@labels = labels
|
49
|
+
@headers = headers
|
50
|
+
@subheaders = subheaders
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_accessor :headers, :subheaders
|
54
|
+
|
55
|
+
def <<(label)
|
56
|
+
unless self.class.labelclasses.any? { |klass| label.is_a?(klass) }
|
57
|
+
fail ArgumentError, "Can only add recognised labels"
|
58
|
+
end
|
59
|
+
if self.map(&:name).include? label.name
|
60
|
+
fail ArgumentError, "Cannot add duplicate labels"
|
61
|
+
end
|
62
|
+
@labels << label
|
63
|
+
end
|
64
|
+
|
65
|
+
def ==(other)
|
66
|
+
@labels.all? do |label|
|
67
|
+
other.find { |l| l == label }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def each(&blk)
|
72
|
+
@labels.each(&blk)
|
73
|
+
end
|
74
|
+
|
75
|
+
def map_int(&blk)
|
76
|
+
self.class.new(@labels.map(&blk), @headers, @subheaders)
|
77
|
+
end
|
78
|
+
|
79
|
+
def select(&blk)
|
80
|
+
self.class.new(@labels.select(&blk), @headers, @subheaders)
|
81
|
+
end
|
82
|
+
|
83
|
+
def reject!(&blk)
|
84
|
+
@labels.reject!(&blk)
|
85
|
+
end
|
86
|
+
|
87
|
+
def reject(&blk)
|
88
|
+
self.class.new(@labels.reject(&blk), @headers, @subheaders)
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete_if(&blk)
|
92
|
+
@labels.delete_if(&blk)
|
93
|
+
end
|
94
|
+
|
95
|
+
def group_by(&blk)
|
96
|
+
@labels.group_by(&blk).map { |k, v| [k, self.class.new(v)] }.to_h
|
97
|
+
end
|
98
|
+
|
99
|
+
def sort_by(&blk)
|
100
|
+
self.class.new(@labels.sort_by(&blk), @headers, @subheaders)
|
101
|
+
end
|
102
|
+
|
103
|
+
def merge(other)
|
104
|
+
LabelListComparison.new(self, other).merge(priority: :right)
|
105
|
+
end
|
106
|
+
|
107
|
+
def merge!(other)
|
108
|
+
merged = merge(other)
|
109
|
+
@labels = merged.to_a
|
110
|
+
@headers = merged.headers
|
111
|
+
@subheaders = merged.subheaders
|
112
|
+
end
|
113
|
+
|
114
|
+
def +(other); merge(other) end
|
115
|
+
|
116
|
+
def to_a
|
117
|
+
@labels
|
118
|
+
end
|
119
|
+
|
120
|
+
def empty?
|
121
|
+
@labels.empty?
|
122
|
+
end
|
123
|
+
|
124
|
+
def contains?(name)
|
125
|
+
map(&:name).include?(name)
|
126
|
+
end
|
127
|
+
|
128
|
+
def to_s
|
129
|
+
"[#{@labels.map(&:to_s).join(", ")}]"
|
130
|
+
end
|
131
|
+
|
132
|
+
def size; count end
|
133
|
+
|
134
|
+
def inspect; to_s end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class Ecu
|
2
|
+
class LabelListComparison
|
3
|
+
|
4
|
+
def initialize(left, right)
|
5
|
+
unless left.is_a?(LabelList) && right.is_a?(LabelList)
|
6
|
+
fail "Can only merge LabelLists (left: #{left.class}, right: #{right.class})!"
|
7
|
+
end
|
8
|
+
|
9
|
+
@left = left.map(&:name).zip(left).to_h
|
10
|
+
@right = right.map(&:name).zip(right).to_h
|
11
|
+
@unified_headers = left.headers + right.headers
|
12
|
+
@unified_subheaders = left.subheaders + right.subheaders
|
13
|
+
@names = Hash.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def names(criterion)
|
17
|
+
@names[criterion] ||= \
|
18
|
+
case criterion
|
19
|
+
when :left_exclusive then left.keys - right.keys
|
20
|
+
when :right_exclusive then right.keys - left.keys
|
21
|
+
when :common then left.keys & right.keys
|
22
|
+
when :nonequal then names(:common).select { |n| left.fetch(n) != right.fetch(n) }
|
23
|
+
when :equal then names(:common).select { |n| left.fetch(n) == right.fetch(n) }
|
24
|
+
else fail "Unknown criterion #{criterion}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def left_exclusive
|
29
|
+
names(:left_exclusive).map { |name| left.fetch(name) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def right_exclusive
|
33
|
+
names(:right_exclusive).map { |name| right.fetch(name) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def differences
|
37
|
+
names(:nonequal).map { |name| [ left.fetch(name), right.fetch(name) ] }
|
38
|
+
end
|
39
|
+
|
40
|
+
def merge(priority: :right)
|
41
|
+
labels = []
|
42
|
+
left.each do |name, label|
|
43
|
+
if names(:common).include?(name) && priority == :right
|
44
|
+
labels << right.fetch(name)
|
45
|
+
else
|
46
|
+
labels << left.fetch(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
right.each do |name, label|
|
50
|
+
next if labels.map(&:name).include?(name)
|
51
|
+
if names(:common).include?(name) && priority == :left
|
52
|
+
labels << left.fetch(name)
|
53
|
+
else
|
54
|
+
labels << right.fetch(name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
LabelList.new(labels, @unified_headers, @unified_subheaders)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
attr_reader :left, :right
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require_relative "label"
|
2
|
+
|
3
|
+
class Ecu
|
4
|
+
class Stuetzstellenverteilung < Label
|
5
|
+
|
6
|
+
attr_reader :xdim, :xvalue, :xunit
|
7
|
+
|
8
|
+
def initialize(name:,
|
9
|
+
xdim:,
|
10
|
+
xvalue:,
|
11
|
+
xunit: nil,
|
12
|
+
function: nil,
|
13
|
+
description: nil)
|
14
|
+
@name = name
|
15
|
+
@xdim = xdim
|
16
|
+
@xvalue = xvalue
|
17
|
+
@xunit = xunit
|
18
|
+
@function = function
|
19
|
+
@description = description
|
20
|
+
|
21
|
+
init_error "Dimensions for xvalue don't match xdim" if xvalue.size != xdim
|
22
|
+
end
|
23
|
+
|
24
|
+
def round_to(n)
|
25
|
+
return self unless xvalue.all? { |x| x.is_a?(Numeric) }
|
26
|
+
self.with \
|
27
|
+
xvalue: xvalue.map { |x| x.round(n) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def bytesize
|
31
|
+
xdim * BYTESIZE[:number]
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s(detail: false)
|
35
|
+
if detail == :value
|
36
|
+
"#{name}:\n#{to_s(detail: :justvalue)}"
|
37
|
+
elsif detail == :justvalue
|
38
|
+
ValuePrinter.call(self)
|
39
|
+
elsif detail == :onelinefull
|
40
|
+
"#{name} #{to_s(detail: :oneline)}"
|
41
|
+
elsif detail == :oneline
|
42
|
+
"(#{xdim}x1) X: #{valuestats(xvalue)}"
|
43
|
+
else
|
44
|
+
"#{name}: #{@xdim}x1 #{type}".tap do |str|
|
45
|
+
if detail
|
46
|
+
str << "\n"
|
47
|
+
str << " Unit: \"#{xunit}\"\n"
|
48
|
+
str << " Value:\n"
|
49
|
+
str << ValuePrinter.call(self).indent(4)
|
50
|
+
str << "\n"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def properties
|
57
|
+
[:name, :xdim, :xvalue, :xunit, :function, :description]
|
58
|
+
end
|
59
|
+
|
60
|
+
def equality_properties
|
61
|
+
[:name, :xvalue]
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|