automotive-ecu 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/bin/bundle +114 -0
  3. data/bin/coderay +29 -0
  4. data/bin/console +14 -0
  5. data/bin/htmldiff +29 -0
  6. data/bin/irb +29 -0
  7. data/bin/ldiff +29 -0
  8. data/bin/pry +29 -0
  9. data/bin/rake +29 -0
  10. data/bin/rspec +29 -0
  11. data/bin/setup +8 -0
  12. data/lib/core_ext.rb +69 -0
  13. data/lib/ecu/combined_list.rb +25 -0
  14. data/lib/ecu/combined_list_comparison.rb +50 -0
  15. data/lib/ecu/interfaces/a2l/label_list.rb +18 -0
  16. data/lib/ecu/interfaces/a2l/signal_list.rb +15 -0
  17. data/lib/ecu/interfaces/dbc/signal_list.rb +12 -0
  18. data/lib/ecu/interfaces/dcm/buffer.rb +39 -0
  19. data/lib/ecu/interfaces/dcm/festkennfeld.rb +9 -0
  20. data/lib/ecu/interfaces/dcm/festkennlinie.rb +9 -0
  21. data/lib/ecu/interfaces/dcm/festwert.rb +20 -0
  22. data/lib/ecu/interfaces/dcm/festwerteblock.rb +26 -0
  23. data/lib/ecu/interfaces/dcm/functions.rb +13 -0
  24. data/lib/ecu/interfaces/dcm/gruppenkennfeld.rb +9 -0
  25. data/lib/ecu/interfaces/dcm/gruppenkennlinie.rb +9 -0
  26. data/lib/ecu/interfaces/dcm/kennfeld.rb +32 -0
  27. data/lib/ecu/interfaces/dcm/kennlinie.rb +25 -0
  28. data/lib/ecu/interfaces/dcm/label.rb +25 -0
  29. data/lib/ecu/interfaces/dcm/label_list.rb +94 -0
  30. data/lib/ecu/interfaces/dcm/malformed_dcm_error.rb +34 -0
  31. data/lib/ecu/interfaces/dcm/property_parser.rb +51 -0
  32. data/lib/ecu/interfaces/dcm/stuetzstellenverteilung.rb +20 -0
  33. data/lib/ecu/interfaces/lab/combined_list.rb +32 -0
  34. data/lib/ecu/interfaces/lab/lab_parser.rb +61 -0
  35. data/lib/ecu/interfaces/lab/label.rb +21 -0
  36. data/lib/ecu/interfaces/lab/label_list.rb +29 -0
  37. data/lib/ecu/interfaces/lab/signal.rb +15 -0
  38. data/lib/ecu/interfaces/lab/signal_list.rb +29 -0
  39. data/lib/ecu/interfaces/mfile/festwert.rb +10 -0
  40. data/lib/ecu/interfaces/mfile/festwerteblock.rb +12 -0
  41. data/lib/ecu/interfaces/mfile/kennfeld.rb +12 -0
  42. data/lib/ecu/interfaces/mfile/kennlinie.rb +7 -0
  43. data/lib/ecu/interfaces/mfile/label_list.rb +13 -0
  44. data/lib/ecu/interfaces/mfile/stuetzstellenverteilung.rb +7 -0
  45. data/lib/ecu/labels/festwert.rb +58 -0
  46. data/lib/ecu/labels/festwerteblock.rb +80 -0
  47. data/lib/ecu/labels/interpolator.rb +55 -0
  48. data/lib/ecu/labels/kennfeld.rb +107 -0
  49. data/lib/ecu/labels/kennlinie.rb +84 -0
  50. data/lib/ecu/labels/label.rb +82 -0
  51. data/lib/ecu/labels/label_list.rb +137 -0
  52. data/lib/ecu/labels/label_list_comparison.rb +65 -0
  53. data/lib/ecu/labels/stuetzstellenverteilung.rb +65 -0
  54. data/lib/ecu/labels/value_comparison.rb +37 -0
  55. data/lib/ecu/labels/value_printer.rb +49 -0
  56. data/lib/ecu/labels.rb +2 -0
  57. data/lib/ecu/signals/signal.rb +73 -0
  58. data/lib/ecu/signals/signal_list.rb +68 -0
  59. data/lib/ecu/signals/signal_list_comparison.rb +52 -0
  60. data/lib/ecu/signals.rb +2 -0
  61. data/lib/ecu/version.rb +3 -0
  62. data/lib/ecu.rb +59 -0
  63. 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