cpee 1.4.8 → 1.4.10

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.
Files changed (98) hide show
  1. checksums.yaml +5 -5
  2. data/cockpit/config.json +6 -0
  3. data/cockpit/css/ui.css +7 -7
  4. data/cockpit/index.html +4 -4
  5. data/cockpit/js/instance.js +6 -3
  6. data/cockpit/themes/compact/theme.js +107 -6
  7. data/cockpit/themes/default/rngs/call.rng +29 -0
  8. data/cockpit/themes/default/rngs/callmanipulate.rng +29 -0
  9. data/cpee.gemspec +1 -1
  10. data/lib/cpee/controller.rb +1 -1
  11. data/lib/cpee/instantiation.rb +44 -21
  12. data/lib/engine/instances.rng +5 -2
  13. data/lib/instantiation.xml +5 -1
  14. data/log/elasticsearch.rb +1 -106
  15. data/log/elasticsearch_logging.rb +237 -0
  16. data/log/legacy/test_es.rb +26 -0
  17. data/log/legacy/test_split.rb +211 -0
  18. data/log/trace.yaml +17717 -0
  19. data/log/trace_sic.yaml +18343 -0
  20. data/server/handlerwrappers/default.rb +28 -23
  21. data/server/instances/1/properties.xml +158 -31
  22. data/server/instances/2/properties.xml +137 -12
  23. data/server/instances/3/properties.xml +116 -80
  24. data/server/resources/base.xml +78 -0
  25. data/server/resources/test.rb +19 -0
  26. data/server/resources/test.xml +33 -0
  27. data/server/resources/topics.xml +6 -3
  28. data/server/resources/transformation_dslx.xsl +5 -0
  29. data/server/server.pid +1 -0
  30. metadata +13 -71
  31. data/log/logs/1/test +0 -1
  32. data/server/instances/10/notifications/182434032285ca1d06a8b6554b8889c8/consumer-secret +0 -1
  33. data/server/instances/10/notifications/182434032285ca1d06a8b6554b8889c8/producer-secret +0 -1
  34. data/server/instances/10/notifications/182434032285ca1d06a8b6554b8889c8/subscription.xml +0 -23
  35. data/server/instances/10/notifications/512a3785631a5245dbb45fa150ea72ed/consumer-secret +0 -1
  36. data/server/instances/10/notifications/512a3785631a5245dbb45fa150ea72ed/producer-secret +0 -1
  37. data/server/instances/10/notifications/512a3785631a5245dbb45fa150ea72ed/subscription.xml +0 -6
  38. data/server/instances/10/properties.xml +0 -187
  39. data/server/instances/11/properties.xml +0 -191
  40. data/server/instances/12/properties.xml +0 -191
  41. data/server/instances/13/properties.xml +0 -204
  42. data/server/instances/14/properties.xml +0 -31
  43. data/server/instances/15/properties.xml +0 -30
  44. data/server/instances/16/properties.xml +0 -29
  45. data/server/instances/17/properties.xml +0 -29
  46. data/server/instances/18/properties.xml +0 -31
  47. data/server/instances/19/properties.xml +0 -31
  48. data/server/instances/20/properties.xml +0 -191
  49. data/server/instances/21/properties.xml +0 -156
  50. data/server/instances/22/properties.xml +0 -118
  51. data/server/instances/23/properties.xml +0 -118
  52. data/server/instances/24/properties.xml +0 -156
  53. data/server/instances/25/properties.xml +0 -156
  54. data/server/instances/26/properties.xml +0 -156
  55. data/server/instances/27/properties.xml +0 -140
  56. data/server/instances/28/properties.xml +0 -156
  57. data/server/instances/29/properties.xml +0 -156
  58. data/server/instances/30/properties.xml +0 -354
  59. data/server/instances/31/properties.xml +0 -354
  60. data/server/instances/313/notifications/crisp/consumer-secret +0 -1
  61. data/server/instances/313/notifications/crisp/producer-secret +0 -1
  62. data/server/instances/313/notifications/crisp/subscription.xml +0 -7
  63. data/server/instances/313/notifications/logging/consumer-secret +0 -1
  64. data/server/instances/313/notifications/logging/producer-secret +0 -1
  65. data/server/instances/313/notifications/logging/subscription.xml +0 -7
  66. data/server/instances/313/notifications/logging_yaml/consumer-secret +0 -1
  67. data/server/instances/313/notifications/logging_yaml/producer-secret +0 -1
  68. data/server/instances/313/notifications/logging_yaml/subscription.xml +0 -7
  69. data/server/instances/313/properties.xml +0 -524
  70. data/server/instances/32/properties.xml +0 -628
  71. data/server/instances/33/properties.xml +0 -156
  72. data/server/instances/4/properties.xml +0 -114
  73. data/server/instances/5/properties.xml +0 -114
  74. data/server/instances/6/properties.xml +0 -206
  75. data/server/instances/662/notifications/crisp/consumer-secret +0 -1
  76. data/server/instances/662/notifications/crisp/producer-secret +0 -1
  77. data/server/instances/662/notifications/crisp/subscription.xml +0 -7
  78. data/server/instances/662/notifications/logging/consumer-secret +0 -1
  79. data/server/instances/662/notifications/logging/producer-secret +0 -1
  80. data/server/instances/662/notifications/logging/subscription.xml +0 -7
  81. data/server/instances/662/notifications/logging_yaml/consumer-secret +0 -1
  82. data/server/instances/662/notifications/logging_yaml/producer-secret +0 -1
  83. data/server/instances/662/notifications/logging_yaml/subscription.xml +0 -7
  84. data/server/instances/662/properties.xml +0 -427
  85. data/server/instances/663/properties.xml +0 -31
  86. data/server/instances/664/properties.xml +0 -425
  87. data/server/instances/665/properties.xml +0 -425
  88. data/server/instances/666/properties.xml +0 -427
  89. data/server/instances/667/properties.xml +0 -427
  90. data/server/instances/668/properties.xml +0 -425
  91. data/server/instances/669/properties.xml +0 -425
  92. data/server/instances/670/properties.xml +0 -31
  93. data/server/instances/671/properties.xml +0 -158
  94. data/server/instances/672/properties.xml +0 -199
  95. data/server/instances/673/properties.xml +0 -52
  96. data/server/instances/7/properties.xml +0 -156
  97. data/server/instances/8/properties.xml +0 -118
  98. data/server/instances/9/properties.xml +0 -120
@@ -19,7 +19,7 @@
19
19
  <element name="instances">
20
20
  <zeroOrMore>
21
21
  <ref name="instance"/>
22
- </zeroOrMore>
22
+ </zeroOrMore>
23
23
  </element>
24
24
  </start>
25
25
 
@@ -28,11 +28,14 @@
28
28
  <attribute name='id'>
29
29
  <data type="integer"/>
30
30
  </attribute>
31
+ <attribute name='uuid'>
32
+ <data type="string"/>
33
+ </attribute>
31
34
  <attribute name='state'>
32
35
  <data type="string"/>
33
36
  </attribute>
34
37
  <data type="string"/>
35
38
  </element>
36
- </define>
39
+ </define>
37
40
 
38
41
  </grammar>
@@ -1,5 +1,4 @@
1
1
  <description datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes" xmlns="http://riddl.org/ns/description/1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
2
-
3
2
  <message name="xmlsimple">
4
3
  <parameter name="xml" mimetype="*/xml"/>
5
4
  </message>
@@ -64,6 +63,11 @@
64
63
  <resource relative="url">
65
64
  <post in="url" out="result"/>
66
65
  </resource>
66
+ <resource relative="callback">
67
+ <resource>
68
+ <post/>
69
+ </resource>
70
+ </resource>
67
71
  </resource>
68
72
 
69
73
  </description>
@@ -1,111 +1,6 @@
1
1
  #!/usr/bin/ruby
2
- require 'pp'
3
- require 'json'
4
- require 'yaml'
5
- require 'rubygems'
6
- require 'riddl/server'
7
- require 'riddl/utils/fileserve'
8
- require 'json'
9
- require 'time'
2
+ require_relative 'elasticsearch_logging'
10
3
 
11
- require 'faraday'
12
- require 'elasticsearch'
13
- require 'logger'
14
-
15
- class Logging < Riddl::Implementation #{{{
16
- def doc(topic,event_name,esc,template,instancenr,notification)
17
- uuid = notification['instance_uuid']
18
- return unless uuid
19
-
20
- activity = notification['activity']
21
- parameters = notification['parameters']
22
- receiving = notification['received']
23
-
24
- log = YAML::load(File.read(template))
25
- log["log"]["trace"]["concept:name"] ||= instancenr.to_i
26
- log["log"]["trace"]["cpee:name"] ||= notification['instance_name'] if notification["instance_name"]
27
- log["log"]["trace"]["cpee:uuid"] ||= notification['instance_uuid'] if notification["instance_uuid"]
28
- unless esc.indices.exists? index: 'trace'
29
- esc.indices.create index: 'trace', body: {
30
- "mappings" => {
31
- "entry" => {
32
- "properties" => {
33
- "concept:name" => {
34
- "type" => "integer"
35
- },
36
- "cpee:name" => {
37
- "type" => "text"
38
- },
39
- "cpee:uuid": {
40
- "type" => "text"
41
- }
42
- }
43
- }
44
- }
45
- }
46
- end
47
-
48
- esc.index index: 'trace', type: 'entry', id: log["log"]["trace"]["cpee:uuid"], body: log["log"]["trace"]
49
- p notification['attributes']
50
-
51
- event = {}
52
- event["trace:id"] = instancenr
53
- event["concept:name"] = notification["label"] if notification["label"]
54
- if notification["endpoint"]
55
- event["concept:endpoint"] = notification["endpoint"]
56
- end
57
- event["id:id"] = (activity.nil? || activity == "") ? 'external' : activity
58
- event["cpee:uuid"] = notification['instance_uuid'] if notification["instance_uuid"]
59
- case event_name
60
- when 'receiving', 'change'
61
- event["lifecycle:transition"] = "unknown"
62
- when 'done'
63
- event["lifecycle:transition"] = "complete"
64
- else
65
- event["lifecycle:transition"] = "start"
66
- end
67
- event["cpee:lifecycle:transition"] = "#{topic}/#{event_name}"
68
- data_send = ((parameters["arguments"].nil? ? [] : parameters["arguments"]) rescue [])
69
- event["list"] = {"data_send" => data_send} unless data_send.empty?
70
- if notification['changed']&.any?
71
- if event.has_key? "list"
72
- event["list"]["data_changed"] ||= notification['changed']
73
- else
74
- event["list"] = {"data_changer" => notification['changed']}
75
- end
76
- end
77
- if notification['values']&.any?
78
- if event.has_key? "list"
79
- event["list"]["data_values"] ||= notification['values']
80
- else
81
- event["list"] = {"data_values" => notification['values']}
82
- end
83
- end
84
- if receiving&.any?
85
- if event.has_key? "list"
86
- event["list"]["data_received"] ||= receiving
87
- else
88
- event["list"] = {"data_receiver" => receiving}
89
- end
90
- end
91
- event["time:timestamp"]= Time.now.iso8601
92
-
93
- iname = "instance_" + notification['instance_name'].downcase.gsub(/[^a-z]/,'_')
94
- esc.indices.create index: iname rescue nil
95
- esc.index index: iname, type: 'entry', body: event
96
- nil
97
- end
98
-
99
- def response
100
- topic = @p[1].value
101
- event_name = @p[2].value
102
- esc = @a[0]
103
- template = @a[1]
104
- instancenr = @h['CPEE_INSTANCE'].split('/').last
105
- notification = JSON.parse(@p[3].value)
106
- doc topic, event_name, esc, template, instancenr, notification
107
- end
108
- end #}}}
109
4
 
110
5
  Riddl::Server.new(File.join(__dir__,'/log.xml'), :host => 'localhost', :port => 9307) do
111
6
  accessible_description true
@@ -0,0 +1,237 @@
1
+ require 'pp'
2
+ require 'time'
3
+ require 'digest'
4
+
5
+ require 'json'
6
+ require 'yaml'
7
+ require 'riddl/server'
8
+ require 'faraday'
9
+ require 'elasticsearch'
10
+
11
+ class Logging < Riddl::Implementation
12
+
13
+ def doc(topic,event_name,esc,template,instancenr,notification)
14
+ uuid = notification['instance_uuid']
15
+ return unless uuid
16
+
17
+ # activity = notification['activity']
18
+ # parameters = notification['parameters']
19
+ # receiving = notification['received']
20
+ # attributes = notification['attributes']
21
+
22
+ unless esc.indices.exists? index: 'artefacts' #{{{
23
+ esc.indices.create index: 'artefacts', body: {
24
+ "mappings" => {
25
+ "entry" => {
26
+ "properties" => {
27
+ "group" => {
28
+ "type" => "keyword"
29
+
30
+ },
31
+ "name" => {
32
+ "type" => "keyword"
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ end #}}}
39
+ unless esc.indices.exists? index: 'instances' #{{{
40
+ esc.indices.create index: 'instances', body: {
41
+ "mappings" => {
42
+ "entry" => {
43
+ "properties" => {
44
+ "uuid" => {
45
+ "type" => "text"
46
+ },
47
+ "group" => {
48
+ "type" => "keyword"
49
+ },
50
+ "name" => {
51
+ "type" => "keyword"
52
+ },
53
+ "date": {
54
+ "type": "date",
55
+ "format": "date_time_no_millis"
56
+ },
57
+ "info": {
58
+ "type": "keyword"
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ end #}}}
65
+ unless esc.indices.exists? index: 'spawned' #{{{
66
+ esc.indices.create index: 'spawned', body: {
67
+ "mappings" => {
68
+ "entry" => {
69
+ "properties" => {
70
+ "uuid" => {
71
+ "type" => "text"
72
+ },
73
+ "spawned_uuid" => {
74
+ "type" => "text"
75
+ },
76
+ "date": {
77
+ "type": "date",
78
+ "format": "date_time_no_millis"
79
+ },
80
+ }
81
+ }
82
+ }
83
+ }
84
+ end #}}}
85
+ unless esc.indices.exists? index: 'sensors' #{{{
86
+ esc.indices.create index: 'sensors', body: {
87
+ "mappings" => {
88
+ "entry" => {
89
+ "properties" => {
90
+ "uuid" => {
91
+ "type" => "text"
92
+ },
93
+ "sensor" => {
94
+ "type" => "keyword"
95
+ },
96
+ "task" => {
97
+ "type" => "keyword"
98
+ },
99
+ "visualizer_url" => {
100
+ "type" => "text"
101
+ },
102
+ "visualizer_params" => {
103
+ "type" => "nested"
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ end #}}}
110
+ unless esc.indices.exists? index: 'values' #{{{
111
+ esc.indices.create index: 'values', body: {
112
+ "mappings" => {
113
+ "entry" => {
114
+ "properties" => {
115
+ "uuid" => {
116
+ "type" => "keyword"
117
+ },
118
+ "sensor" => {
119
+ "type" => "keyword"
120
+ },
121
+ "task" => {
122
+ "type" => "keyword"
123
+ },
124
+ "timestamp": {
125
+ "type": "date",
126
+ "format": "date_time"
127
+ },
128
+ "value" => {
129
+ "type" => "text",
130
+ "fields" => {
131
+ "value_f" => {
132
+ "type" => "float",
133
+ "store" => true
134
+ },
135
+ "value_l" => {
136
+ "type" => "long",
137
+ "store" => true
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ end #}}}
146
+
147
+ uuid = notification.dig('instance_uuid')
148
+
149
+ if notification.dig('attributes','artefacts') #{{{
150
+ artefacts = JSON.parse(notification.dig('attributes','artefacts'))
151
+
152
+ artefacts.each do |a|
153
+ aid = Digest::MD5.hexdigest(a['group'] + '_' + a['name'])
154
+ iid = Digest::MD5.hexdigest(a['group'] + '_' + a['name'] + '_' + uuid)
155
+ esc.index index: 'artefacts', type: 'entry', id: aid, body: a
156
+ esc.index index: 'instances', type: 'entry', id: iid, body: {
157
+ 'uuid': uuid,
158
+ 'group': a['group'],
159
+ 'name': a['name'],
160
+ 'date': Time.now.iso8601,
161
+ 'info': notification.dig('attributes','info')
162
+ }
163
+
164
+ end
165
+ end #}}}
166
+
167
+ case "#{topic}/#{event_name}"
168
+ when "dataelements/change", "endpoints/change"
169
+ sensors = JSON.parse(notification.dig('attributes','sensors') || '[]')
170
+ sensors.each do |s|
171
+ sid = Digest::MD5.hexdigest(uuid + '_' + s['name'])
172
+ esc.index index: 'sensors', type: 'entry', id: sid, body: {
173
+ 'uuid': uuid,
174
+ 'sensor': s['name'],
175
+ 'visualizer_url': s['visualizer_url'],
176
+ 'visualizer_params': [s['visualizer_params']]
177
+ }
178
+ esc.index index: 'values', type: 'entry', body: {
179
+ 'uuid': uuid,
180
+ 'sensor': s['name'],
181
+ 'timestamp': notification.dig('timestamp'),
182
+ 'value': s['value']
183
+ }
184
+ end
185
+ when "activity/receiving"
186
+ sensors = JSON.parse(notification.dig('sensors') || '[]')
187
+ tdoc = notification.dig('received')
188
+ sensors.each do |s|
189
+ sid = Digest::MD5.hexdigest(uuid + '_' + s['name'])
190
+ esc.index index: 'sensors', type: 'entry', id: sid, body: {
191
+ 'uuid': uuid,
192
+ 'sensor': s['name'],
193
+ 'task': notification.dig('activity'),
194
+ 'visualizer_url': s['visualizer_url'],
195
+ 'visualizer_params': (s['visualizer_params'].nil? || s['visualizer_params'].empty? ? [] : [s['visualizer_params']])
196
+ }
197
+ status, result = Riddl::Client.new(s['extractor_url']).post [
198
+ Riddl::Parameter::Simple.new('data',JSON.pretty_generate(tdoc)),
199
+ Riddl::Parameter::Simple.new('what',s['extractor_arg'])
200
+ ]
201
+ if status >= 200 && status < 300
202
+ ret = JSON::parse(result[0]&.value.read) rescue []
203
+ ret.each do |v,t|
204
+ esc.index index: 'values', type: 'entry', body: {
205
+ 'uuid': uuid,
206
+ 'sensor': s['name'],
207
+ 'timestamp': t,
208
+ 'value': v
209
+ }
210
+ end
211
+ end
212
+
213
+ end
214
+ end
215
+ nil
216
+ end
217
+
218
+ def response #{{{
219
+ ### save events for later replay
220
+ # a = {
221
+ # :topic => @p[1].value,
222
+ # :event_name => @p[2].value,
223
+ # :instancenr => @h['CPEE_INSTANCE'].split('/').last,
224
+ # :notification => JSON.parse(@p[3].value)
225
+ # }.to_yaml
226
+ # File.open('events.yaml','a') do |f|
227
+ # f << a
228
+ # end
229
+ topic = @p[1].value
230
+ event_name = @p[2].value
231
+ esc = @a[0]
232
+ template = @a[1]
233
+ instancenr = @h['CPEE_INSTANCE'].split('/').last
234
+ notification = JSON.parse(@p[3].value)
235
+ doc topic, event_name, esc, template, instancenr, notification
236
+ end #}}}
237
+ end
@@ -0,0 +1,26 @@
1
+ require 'faraday'
2
+ require 'elasticsearch'
3
+ require 'logger'
4
+
5
+ client = Elasticsearch::Client.new hosts: ['localhost:8400']
6
+ unless client.indices.exists? index: 'trace'
7
+ client.indices.create index: 'trace', body: {
8
+ "mappings" => {
9
+ "entry" => {
10
+ "properties" => {
11
+ "concept:name" => {
12
+ "type" => "integer"
13
+ },
14
+ "cpee:name" => {
15
+ "type" => "text"
16
+ },
17
+ "cpee:uuid": {
18
+ "type" => "text"
19
+ }
20
+ }
21
+ }
22
+ }
23
+ }
24
+ end
25
+
26
+ client.index index: 'trace', type: 'entry', id: , body: log["log"]["trace"]
@@ -0,0 +1,211 @@
1
+ require 'yaml'
2
+ require 'pp'
3
+
4
+ #{{{
5
+ yaml1 = <<-END
6
+ event:
7
+ cpee:lifecycle:transition: activity/receiving
8
+ list:
9
+ data_receiver:
10
+ - message:
11
+ mimetype: application/json
12
+ content:
13
+ - ID: ns=2;s=/Channel/ProgramInfo/actBlock
14
+ meta:
15
+ StatusCodea: Good
16
+ - ID: ns=2;s=/Channel/Spindle/driveLoad
17
+ meta:
18
+ StatusCodeb: Good
19
+ - message:
20
+ mimetype: application/xml
21
+ content:
22
+ - ID: ns=2;s=/Channel/ProgramInfo/actBlock
23
+ meta:
24
+ StatusCodec: Good
25
+ - ID: ns=2;s=/Channel/Spindle/driveLoad
26
+ meta:
27
+ StatusCoded: Good
28
+ time:timestamp: '2018-05-03T14:08:14+02:00'
29
+ END
30
+ #}}}
31
+ #{{{
32
+ yaml2 = <<-END
33
+ event:
34
+ trace:id: '160'
35
+ concept:name: Fetch
36
+ concept:endpoint: https://centurio.work/data/mt45/queue/48623a67-7b67-4902-b5d4-7243f1d090e2/push
37
+ id:id: a1
38
+ lifecycle:transition: unknown
39
+ cpee:lifecycle:transition: activity/receiving
40
+ list:
41
+ data_receiver:
42
+ - message:
43
+ mimetype: application/json
44
+ content:
45
+ - ID: ns=2;s=/Channel/ProgramInfo/actBlock
46
+ source: opcua
47
+ name: Program/actBlock
48
+ description: Current part program block.
49
+ path: "/Object/Sinumerik/Channel/ProgramInfo/actBlock"
50
+ value:
51
+ timestamp: '2018-05-03 14:16:55.241000'
52
+ meta:
53
+ StatusCode: Good
54
+ ServerTimestamp: '2018-05-03 12:16:55.318853'
55
+ VariantType: VariantType.String
56
+ ClientHandle: '212'
57
+ - ID: ns=2;s=/Channel/Spindle/driveLoad
58
+ source: opcua
59
+ name: Spindle/driveLoad
60
+ description: Load
61
+ path: "/Object/Sinumerik/Channel/Spindle/driveLoad"
62
+ value: 0.030517578125
63
+ timestamp: '2018-05-03 14:16:55.241000'
64
+ meta:
65
+ StatusCode: Good
66
+ ServerTimestamp: '2018-05-03 12:16:55.318853'
67
+ VariantType: VariantType.Double
68
+ ClientHandle: '217'
69
+ - ID: ns=2;s=/Channel/MachineAxis/aaLeadP[u1,3]
70
+ source: opcua
71
+ name: Axis/Z/aaLeadP
72
+ description: ''
73
+ path: "/Object/Sinumerik/Channel/MachineAxis/aaLeadP[u1,3]"
74
+ value: 162.51611
75
+ timestamp: '2018-05-03 14:16:55.241000'
76
+ meta:
77
+ StatusCode: Good
78
+ ServerTimestamp: '2018-05-03 12:16:55.318853'
79
+ VariantType: VariantType.Double
80
+ ClientHandle: '222'
81
+ - ID: ns=2;s=/Channel/MachineAxis/aaTorque[u1,1]
82
+ source: opcua
83
+ name: Axis/X/aaTorque
84
+ description: ''
85
+ path: "/Object/Sinumerik/Channel/MachineAxis/aaTorque[u1,1]"
86
+ value: -2.072
87
+ timestamp: '2018-05-03 14:16:55.241000'
88
+ meta:
89
+ StatusCode: Good
90
+ ServerTimestamp: '2018-05-03 12:16:55.318853'
91
+ VariantType: VariantType.Double
92
+ ClientHandle: '223'
93
+ - ID: ns=2;s=/Channel/MachineAxis/aaTorque[u1,2]
94
+ source: opcua
95
+ name: Axis/Y/aaTorque
96
+ description: ''
97
+ path: "/Object/Sinumerik/Channel/MachineAxis/aaTorque[u1,2]"
98
+ value: 0.107
99
+ timestamp: '2018-05-03 14:16:55.241000'
100
+ meta:
101
+ StatusCode: Good
102
+ ServerTimestamp: '2018-05-03 12:16:55.318853'
103
+ VariantType: VariantType.Double
104
+ ClientHandle: '224'
105
+ time:timestamp: '2018-05-03T14:08:14+02:00'
106
+ END
107
+ #}}}
108
+
109
+ def traverse(node,paths=[[]],anal=[],depth=0)
110
+ cpath = paths.last.dup
111
+ case node
112
+ when Hash
113
+ node.each do |k,v|
114
+ unless cpath.empty?
115
+ paths.last << [] unless paths.last.last.class == Array
116
+ paths << cpath.dup
117
+ end
118
+ paths.last << k
119
+ traverse(v,paths,anal,depth+1)
120
+ end
121
+ when Array
122
+ node.each_with_index do |e,i|
123
+ posanal = [depth,paths.length,nil,[]]
124
+ anal << posanal
125
+
126
+ unless cpath.empty?
127
+ paths.last << [] unless paths.last.last.class == Array
128
+ paths << cpath.dup
129
+ end
130
+ paths.last << i
131
+ traverse(e,paths,posanal.last,depth+1)
132
+ dp = cpath.dup
133
+ dp << [] unless dp.last.class == Array
134
+ paths << dp unless paths.include?(dp)
135
+
136
+ posanal[2] = paths.length - 1
137
+ end
138
+ else
139
+ paths.last << [] unless paths.last.last.class == Array
140
+ end
141
+ end
142
+
143
+ def duplicate(doc,paths,anal)
144
+ res = []
145
+ deep_cloned = Marshal::load(Marshal.dump(paths))
146
+ anal.each_with_index do |e,ei|
147
+ local_cloned = Marshal::load(Marshal.dump(deep_cloned))
148
+ anal.select{ |a| a == e }.each do |a|
149
+ (a[1]).upto(a[2]) do |i|
150
+ local_cloned[i].last << a[0]
151
+ end
152
+ end
153
+ anal.reject{ |a| a == e }.each do |a|
154
+ (a[1]).upto(a[2]) do |i|
155
+ local_cloned[i] = nil
156
+ end
157
+ end
158
+ if !e[3]&.empty?
159
+ e[3..-1].each_with_index do |ee,eei|
160
+ ret = duplicate(doc,local_cloned,ee)
161
+ res.concat ret
162
+ end
163
+ else
164
+ res << extract_from_doc(doc,local_cloned.compact)
165
+ end
166
+ end
167
+ res
168
+ end
169
+
170
+ def extract_from_doc(doc,paths)
171
+ ret = {}
172
+ paths.each do |p|
173
+ next if p.nil?
174
+ a = doc.dig(*p[0..-2])
175
+
176
+ py = p.dup
177
+ p[-1].each_with_index do |px,i|
178
+ py.delete_at(px-i)
179
+ end
180
+
181
+ x1 = py[-2]
182
+ x2 = py[0..-3]
183
+ where = ret
184
+ if x2.any?
185
+ where = ret.dig(*x2)
186
+ end
187
+ where[x1] = {}
188
+
189
+ unless a.class == Hash || a.class == Array
190
+ where[x1] = a
191
+ end
192
+ end
193
+ ret
194
+ end
195
+
196
+ doc = YAML.load(yaml2)
197
+
198
+ paths = [[]]
199
+ anal = []
200
+ traverse(doc,paths,anal)
201
+ anal.uniq!
202
+
203
+ paths.each do |p|
204
+ p p
205
+ end
206
+
207
+ res = duplicate(doc,paths,anal)
208
+
209
+ res.each do |r|
210
+ pp r
211
+ end