cpee 1.4.8 → 1.4.10

Sign up to get free protection for your applications and to get access to all the features.
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