dcm 0.1.40 → 0.1.41

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d85523d78e04f2eeb5e8bf38d9ee2658398d0e308f4f0ca7e137ea5dbe131714
4
- data.tar.gz: 269fc302304ec5501dc6e2e83a19c2584a999e781f44fb2503c7287cc77228cd
3
+ metadata.gz: 893d24fa86203061555148240254ace73ba85975d36f9449cd74f599c88ee08e
4
+ data.tar.gz: 89b17bfb7cee34c53740ef746caed7dc04dad53db24cf261b186ce561f0b870c
5
5
  SHA512:
6
- metadata.gz: 3f0208509522fc67cbeea93a25a722c056d6f5781111ec8f4b931aa52188030f662a95d15710b96f663be8d8c5c591c79b5adbb1913e01ad84ad6a9d342b2442
7
- data.tar.gz: 11e4795e958bf596dc3f3bbb9fc459ecc7433bf250ab71bd3e4dbf65bc34ad0bf3900fbb894f348f63996d7562aeaacd0c4da1fc75c37e7319e102cc9d89933c
6
+ metadata.gz: e0288e78d05b9839d1ba4fc52613122f42b998143e6809ec940755325386c94c877d7347afafaf40f5c3d97fa0a2d7ed28eecfad897ad337093b8f98fef16b4f
7
+ data.tar.gz: d7d35172ec067a7ec121f4d5412a763f477f2cd5a9723f9daa1b9893b0dbf4748153d014af85dce13dbcf0173db5cda43733e1cca14c6fb8e41727cd4cb9246f
data/lib/codinginfo.rb CHANGED
@@ -1,51 +1,21 @@
1
1
  require_relative "file_cache"
2
2
 
3
3
  class Codinginfo
4
- CVAR_PREFIX_REGEXP = /^CVAR_[A-Za-z0-9_]+_\d+\./
4
+ CVAR_PREFIX_REGEXP = /\ACVAR_[A-Za-z0-9_]+_\d+\./
5
5
  PARAM_ASSIGNMENT_HEADERS = { name: /Parameter/, cvar: /Schalter/, package: /Komponente|Component/ }
6
6
 
7
- class Cvar
8
-
9
- # The CVAR must have a name and _may_ have a package and value.
10
- # The package and value can be merged
11
-
12
- attr_reader :name, :value, :package
13
- attr_accessor :assignments
14
- def initialize(name:, package: nil, value: nil)
15
- @name = name
16
- @package = package
17
- @value = value
18
- @assignments = []
19
- end
20
-
21
- def match?(label)
22
- # assignments
23
- # .values
24
- # .select { _1.name == name }
25
- # .map { combine(_1) }
26
- # .any? { label.name.match?(_1.label_prefix) }
27
- label.name.match?(label_prefix)
28
- end
29
-
30
- def label_prefix
31
- @label_prefix ||= if package
32
- [name, package, value].join("_") + "."
33
- else
34
- /^#{name}_([A-Za-z0-9]+_)?#{value}\./
35
- end
36
- end
37
-
38
- def to_s = "#{name}=#{value}"
39
- def ==(other) = name == other.name
40
-
41
- def combine(other)
42
- fail "Cannot combine #{name} with #{other.name}" unless self == other
43
-
44
- self.class.new \
45
- name: name,
46
- package: package || other.package,
47
- value: value || other.value
48
- end
7
+ # Coded labels carry the CVAR value of their variant in the name:
8
+ #
9
+ # CVAR_Market_FooBar_1.PAR_FooBar_cnt_DeacCyc with package
10
+ # CVAR_Obd_3.Foo_Supported without package
11
+ # \_______________/ \_______________________/
12
+ # prefix parameter
13
+ #
14
+ # The spreadsheet assigns each parameter to a CVAR (and package) and
15
+ # lists each variant's CVAR values. Together they define the prefix.
16
+
17
+ Assignment = Data.define(:cvar, :package) do
18
+ def prefix(value) = [cvar, package, value].compact.join("_") + "."
49
19
  end
50
20
 
51
21
  class Variant
@@ -53,10 +23,10 @@ class Codinginfo
53
23
  @properties = headers.zip(row).to_h
54
24
  end
55
25
 
56
- def cvars
57
- @cvars ||= @properties
26
+ def cvar_values
27
+ @cvar_values ||= @properties
58
28
  .select { |key, _| key.start_with?("CVAR_") }
59
- .map { Cvar.new(name: _1, value: _2.to_i) }
29
+ .transform_values(&:to_i)
60
30
  end
61
31
 
62
32
  def attributes
@@ -66,27 +36,42 @@ class Codinginfo
66
36
  .select { |_, val| val == 0 || val == 1 || val == "-" }
67
37
  end
68
38
 
39
+ # Is the label coded for one of this variant's CVAR values? The package
40
+ # is unknown here, so any single-word package is accepted.
69
41
  def has_matching_cvar?(label)
70
- cvars.any? { _1.match?(label) }
42
+ matching_prefix.match?(label.name)
43
+ end
44
+
45
+ def prefix_for(assignment)
46
+ value = cvar_values.fetch(assignment.cvar) do
47
+ fail "Variant #{description} has no value for #{assignment.cvar}"
48
+ end
49
+
50
+ assignment.prefix(value)
71
51
  end
72
52
 
73
53
  def description = @properties[:swfk_id]
54
+
55
+ private
56
+
57
+ def matching_prefix
58
+ @matching_prefix ||= cvar_values
59
+ .map { |cvar, value| "#{Regexp.escape(cvar)}_(?:[A-Za-z0-9]+_)?#{value}" }
60
+ .then { /\A(?:#{_1.join("|")})\./ }
61
+ end
74
62
  end
75
63
 
76
64
  def initialize(filepath, filter, nocache: false)
77
65
  @filter = filter
78
- @variants = []
79
66
 
80
67
  parsed = nocache ? parse_xlsx(filepath) : FileCache.get(filepath) { parse_xlsx(filepath) }
81
68
 
82
69
  @headers = parsed[:headers]
83
70
  @assignments = parsed[:assignments]
84
71
 
85
- parsed[:rows].each do |row|
86
- next unless filter.match?(@headers, row)
87
-
88
- @variants << Variant.new(@headers, row)
89
- end
72
+ @variants = parsed[:rows]
73
+ .select { filter.match?(@headers, _1) }
74
+ .map { Variant.new(@headers, _1) }
90
75
  end
91
76
 
92
77
  def variant
@@ -107,21 +92,20 @@ class Codinginfo
107
92
  def unflatten_list(list)
108
93
  fail "No variants found matching #{@filter}" if @variants.empty?
109
94
 
110
- Ecu::LabelList.new \
111
- list.flat_map { |label|
112
- if has_cvar_assignment?(label)
113
- @variants.map { label.with(name: add_cvar_prefix(label.name, _1)) }
114
- else
115
- label
116
- end
117
- }.uniq,
118
- variants_description
95
+ labels = list.flat_map do |label|
96
+ next label unless has_cvar_assignment?(label)
97
+
98
+ @variants.map { add_cvar_prefix(label, _1) }
99
+ end
100
+
101
+ # Variants sharing a CVAR value yield identical labels
102
+ Ecu::LabelList.new(labels.uniq(&:name), variants_description)
119
103
  end
120
104
 
121
105
  def flatten_list(list)
122
106
  list
123
107
  .select { !cvar_coded?(_1) || variant.has_matching_cvar?(_1) }
124
- .map_int { _1.with(name: remove_cvar_prefix(_1.name)) }
108
+ .map_int { remove_cvar_prefix(_1) }
125
109
  .with(headers: variants_description)
126
110
  end
127
111
 
@@ -139,16 +123,16 @@ class Codinginfo
139
123
  @assignments.key?(label.name)
140
124
  end
141
125
 
142
- def add_cvar_prefix(name, variant)
143
- variant
144
- .cvars
145
- .find { _1 == @assignments[name] }
146
- .then { _1.combine(@assignments[name]) } # this adds the package to the CVar
147
- .then { _1.label_prefix + name }
126
+ def add_cvar_prefix(label, variant)
127
+ prefix = variant.prefix_for(@assignments.fetch(label.name))
128
+
129
+ label.with(name: prefix + label.name)
148
130
  end
149
131
 
150
- def remove_cvar_prefix(name)
151
- name.sub(CVAR_PREFIX_REGEXP, "")
132
+ def remove_cvar_prefix(label)
133
+ return label unless cvar_coded?(label)
134
+
135
+ label.with(name: label.name.sub(CVAR_PREFIX_REGEXP, ""))
152
136
  end
153
137
 
154
138
  private
@@ -176,16 +160,25 @@ class Codinginfo
176
160
  doc.sheets
177
161
  .find { _1.name == "Parameterdefinition" }
178
162
  .tap { fail "Cannot find sheet Parameterdefinition" if _1.nil? }
163
+ .tap { fail "Cannot find headers for Parameterdefinition" unless param_headers_present?(_1.rows.first) }
179
164
  .rows
180
165
  .each(headers: PARAM_ASSIGNMENT_HEADERS) do |row|
181
166
  next if row[:cvar].nil?
182
167
 
183
- assignments[row[:name]] = Cvar.new(name: row[:cvar], package: row[:package])
168
+ assignments[row[:name]] = Assignment.new(row[:cvar], row[:package])
184
169
  end
185
170
 
186
171
  { headers: headers, rows: rows, assignments: assignments }
187
172
  end
188
173
 
174
+ # The reader accepts a header row matching at least one header; a misspelt
175
+ # one would otherwise surface much later as nil values.
176
+ def param_headers_present?(header_row)
177
+ return false unless header_row
178
+
179
+ PARAM_ASSIGNMENT_HEADERS.values.all? { |regexp| header_row.any? { _1.match?(regexp) } }
180
+ end
181
+
189
182
  def parse_headers!(headers, row)
190
183
  return false unless row[1]&.match?(/SWFK-ID/)
191
184
  return false unless row[1]&.match?(/CVAR_BeguData/)
data/lib/file_cache.rb CHANGED
@@ -25,11 +25,10 @@ class FileCache
25
25
  end
26
26
 
27
27
  def cache!
28
- return read_cache if cached?
28
+ hit = read_cache if cached?
29
+ return hit unless hit.nil?
29
30
 
30
31
  yield.tap { write_cache(_1) }
31
- rescue
32
- yield
33
32
  end
34
33
 
35
34
  def clear!
@@ -50,12 +49,17 @@ class FileCache
50
49
  File.mtime(filepath) < File.mtime(cachefilepath)
51
50
  end
52
51
 
52
+ # An unreadable cache (e.g. written by an older version) counts as a miss
53
53
  def read_cache
54
- Marshal.load(File.open(cachefilepath, "rb") { _1.read })
54
+ Marshal.load(File.binread(cachefilepath))
55
+ rescue ArgumentError, TypeError, EOFError, SystemCallError
56
+ nil
55
57
  end
56
58
 
57
59
  def write_cache(obj)
58
- File.open(cachefilepath, "wb") { _1.write(Marshal.dump(obj)) }
60
+ File.binwrite(cachefilepath, Marshal.dump(obj))
61
+ rescue SystemCallError
62
+ # A failed write must not fail the command
59
63
  end
60
64
 
61
65
  def cachefilepath
data/lib/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dcm
2
- VERSION = "0.1.40"
2
+ VERSION = "0.1.41"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dcm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.40
4
+ version: 0.1.41
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Mueller