nwrfc 0.0.1 → 0.0.2

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.
data/README.rdoc CHANGED
@@ -77,6 +77,17 @@ so that ["system2"] at the end points to whatever label you gave it in the YAML
77
77
 
78
78
  == Release Notes
79
79
 
80
+ === What's new in 0.0.2
81
+
82
+ * More comprehensive type support
83
+ * More table operations
84
+
85
+ === What's new in 0.0.1
86
+
87
+ * Fix loading sapnw library
88
+ * Fix UTF-8 conversion for Windows
89
+ * Enhanced type support
90
+
80
91
  === What's new in 0.0.0
81
92
 
82
93
  * Able to call functions
@@ -85,6 +96,6 @@ so that ["system2"] at the end points to whatever label you gave it in the YAML
85
96
 
86
97
  == Contributing
87
98
 
88
- Improvements to the source code are welcome. Email me at martin dot ceronio at infosize dot co dot za.
89
-
90
- Also let me know, please on which platforms you have tested the gem.
99
+ * Improvements to the source code are welcome
100
+ * Indicate on which platforms you have tested the gem
101
+ * Suggestions for improving the API / Hints for idiomatic Ruby
data/lib/nwrfc.rb CHANGED
@@ -7,6 +7,8 @@
7
7
 
8
8
  require File.dirname(__FILE__)+'/nwrfc/nwrfclib'
9
9
  require File.dirname(__FILE__)+'/nwrfc/datacontainer'
10
+ require File.dirname(__FILE__)+'/nwrfc/server'
11
+ require File.dirname(__FILE__)+'/nwrfc/nwerror'
10
12
 
11
13
  require 'date'
12
14
  require 'time'
@@ -38,9 +40,26 @@ module NWRFC
38
40
  [version.read_string_dn.uC, major.read_uint, minor.read_uint, patch.read_uint]
39
41
  end
40
42
 
43
+ # Take Hash of connection parameters and returns FFI pointer to an array
44
+ # for passing to connection
45
+ def NWRFC.make_conn_params(params) #https://github.com/ffi/ffi/wiki/Structs
46
+ par = FFI::MemoryPointer.new(NWRFCLib::RFCConnParam, params.length)
47
+ pars = params.length.times.collect do |i|
48
+ NWRFCLib::RFCConnParam.new(par + i * NWRFCLib::RFCConnParam.size)
49
+ end
50
+ tpar = params.to_a
51
+ params.length.times do |n|
52
+ pars[n][:name] = FFI::MemoryPointer.from_string(tpar[n][0].to_s.cU)
53
+ pars[n][:value] = FFI::MemoryPointer.from_string(tpar[n][1].to_s.cU)
54
+ end
55
+ par
56
+ end
57
+
41
58
  def NWRFC.check_error(error_handle)
42
- raise "Error code #{error_handle[:code]} group #{error_handle[:group]} message #{error_handle[:message].get_str}" \
59
+ raise NWError, error_handle \
43
60
  if error_handle[:code] > 0
61
+ #raise "Error code #{error_handle[:code]} group #{error_handle[:group]} message #{error_handle[:message].get_str}" \
62
+
44
63
  end
45
64
 
46
65
  # Represents a connection to a SAP system that can be used to invoke
@@ -54,11 +73,9 @@ module NWRFC
54
73
  def initialize(conn_params)
55
74
  conn_params.untaint #For params loaded from file, e.g.
56
75
  raise "Connection parameters must be a Hash" unless conn_params.instance_of? Hash
57
- #NWRFCLib.init
58
- @cparams = NWRFCLib.make_conn_params(conn_params)
76
+ @cparams = NWRFC.make_conn_params(conn_params)
59
77
  raise "Could not create valid pointer from parameters" unless @cparams.instance_of? FFI::MemoryPointer
60
- #@errp = FFI::MemoryPointer.new(NWRFCLib::RFCError)
61
- @error = NWRFCLib::RFCError.new #@errp
78
+ @error = NWRFCLib::RFCError.new
62
79
  @handle = NWRFCLib.open_connection(@cparams, conn_params.length, @error.to_ptr)
63
80
  NWRFC.check_error(@error)
64
81
  self
@@ -108,8 +125,15 @@ module NWRFC
108
125
  attr_accessor :handle
109
126
 
110
127
  # Create a parameter by setting parameter attributes
111
- def initialize(*args)
128
+ def initialize(*args)
129
+
112
130
  attr = args[0]
131
+
132
+ #TODO: For certain types, e.g. :RFCTYPE_BCD, a length specification is
133
+ # required, otherwise a segfault is the result later down the line.
134
+ # Find and implement all the types where this is required
135
+ raise "RFCTYPE_BCD requires a length" if attr[:type] == :RFCTYPE_BCD && !(attr[:length])
136
+
113
137
  @handle = NWRFCLib::RFCFuncParam.new
114
138
  @handle[:name] = attr[:name].cU if attr[:name]
115
139
  @handle[:direction] = NWRFCLib::RFC_DIRECTION[attr[:direction]] if attr[:direction]
@@ -133,8 +157,10 @@ module NWRFC
133
157
  # or by calling Connection#get_function. This only represents the description of the function;
134
158
  # to call a function, an instance of a function call must be obtained with #get_function_call
135
159
  class Function
136
- attr_reader :desc, :connection, :function_name
160
+ attr_reader :desc, :function_name
161
+ attr_accessor :connection
137
162
 
163
+ # TODO: Update doc to reflect different calling options
138
164
  # Get a function module instance; can also be obtained by calling Connection#get_function
139
165
  # Takes either: (connection, function_name) or (function_name)
140
166
  # When passed only `function_name`, creates a new function description locally, instead of
@@ -173,17 +199,47 @@ module NWRFC
173
199
  pcount.read_uint
174
200
  end
175
201
 
202
+ # Return the description of parameters associated with this Function
203
+ def parameters
204
+ parameter_count.times.inject({}) do |params, index|
205
+ param = NWRFCLib::RFCFuncParam.new
206
+ NWRFCLib.get_parameter_desc_by_index(@desc, index, param.to_ptr, @error.to_ptr)
207
+ params[param[:name].get_str] = {
208
+ :type => NWRFCLib::RFC_TYPE[param[:type]],
209
+ :direction => NWRFCLib::RFC_DIRECTION[param[:direction]],
210
+ :nucLength => param[:nucLength],
211
+ :ucLength => param[:ucLength],
212
+ :decimals => param[:decimals],
213
+ :typeDescHandle => param[:typeDescHandle],
214
+ :defaultValue => param[:defaultValue].get_str,
215
+ :parameterText => param[:parameterText].get_str,
216
+ :optional => param[:optional]
217
+ }
218
+ params
219
+ end
220
+ end
221
+
176
222
  end
177
223
 
224
+ # Represents a callable instance of a function
178
225
  class FunctionCall < DataContainer
179
226
  attr_reader :handle, :desc, :connection, :function
180
227
 
181
- def initialize(function)
228
+ # TODO: Update doc to reflect different calling options
229
+ # Call with either Function or Connection and Function Call instance (handle)
230
+ def initialize(*args)
231
+ raise("Must initialize function with 1 or 2 arguments") if args.size != 1 && args.size != 2
182
232
  @error = NWRFCLib::RFCError.new
183
- @function = function
184
- @connection = function.connection
185
- @handle = NWRFCLib.create_function(@function.desc, @error.to_ptr)
186
- @desc = function.desc
233
+ if args.size == 2
234
+ @handle = args[1]
235
+ @connection = args[0].connection
236
+ # TODO: Get function description from instance
237
+ else
238
+ @function = args[0] #function
239
+ @connection = args[0].connection
240
+ @handle = NWRFCLib.create_function(@function.desc, @error.to_ptr)
241
+ @desc = args[0].desc
242
+ end
187
243
  NWRFC.check_error(@error)
188
244
  end
189
245
 
@@ -198,7 +254,8 @@ module NWRFC
198
254
 
199
255
  include Enumerable
200
256
 
201
- def each(&block)
257
+ # Iterate over the rows in a table. Each row is yielded as a structure
258
+ def each(&block) #:yields row
202
259
  rc = NWRFCLib.move_to_first_row(@handle, @error)
203
260
  NWRFC.check_error(@error) if rc > 0
204
261
  size.times do |row|
@@ -210,6 +267,7 @@ module NWRFC
210
267
  end
211
268
  end
212
269
 
270
+ # Return the number of rows in the table
213
271
  def size
214
272
  rows = FFI::MemoryPointer.new(:uint)
215
273
  rc = NWRFCLib.get_row_count(@handle, rows, @error)
@@ -231,6 +289,20 @@ module NWRFC
231
289
  Structure.new(struct_handle)
232
290
  end
233
291
 
292
+ # Append a row (structure) to the table
293
+ def append(row)
294
+ raise "Must append a structure" unless row.class == NWRFC::Structure
295
+ rc = NWRFCLib.append_row(@handle, row.handle, @error)
296
+ NWRFC.check_error(@error) if rc > 0
297
+ end
298
+
299
+ # Add new (empty) row and return the structure handle
300
+ def new_row
301
+ s_handle = NWRFCLib.append_new_row(@handle, @error)
302
+ NWRFC.check_error(@error)
303
+ Structure.new(s_handle)
304
+ end
305
+
234
306
  end #class Table
235
307
 
236
308
  # Represents a structure. An instance is obtained internally by passing the
@@ -241,7 +313,7 @@ module NWRFC
241
313
  # Return a list (array) of symbols representing the names of the fields
242
314
  # of this structure
243
315
  #---
244
- # TODO: This is not working!
316
+ # FIXME: This is not working!
245
317
  def fields
246
318
  fc = FFI::MemoryPointer.new(:uint)
247
319
  rc = NWRFCLib.get_field_count(@handle, fc, @error)
@@ -12,157 +12,225 @@ module NWRFC
12
12
  NWRFC.check_error(@error)
13
13
  end
14
14
 
15
- # Return the member specified by string or symbol
15
+ #--
16
+ # VALUE RETRIEVAL
17
+ #++
18
+
19
+ # Get value from a data container (structure, function instance or table)
16
20
  def [](element)
17
21
  member = element.to_s.upcase
18
22
  metadata = member_metadata(element)
19
23
  case metadata[:type]
20
- when :RFCTYPE_CHAR
21
- # TODO: Try use :string parameter in get_chars
22
- return read_chars(metadata)
23
- when :RFCTYPE_DATE
24
- return Date.parse(read_chars(metadata))
24
+
25
+ when :RFCTYPE_CHAR
26
+ # TODO: Try use :string parameter in get_chars
27
+ return read_chars(metadata)
28
+
29
+ when :RFCTYPE_DATE
30
+ return Date.parse(read_chars(metadata))
25
31
  #return Date.new(date[0..3].to_i, date[4..5].to_i, date[6..7].to_i)
26
- when :RFCTYPE_BCD
27
- size = metadata[:ucLength]
28
- cb = FFI::MemoryPointer.new :char, size * 2
29
- rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, cb, size * 2, @error.to_ptr)
30
- NWRFC.check_error(@error) if rc > 0
31
- cb.read_string(size).uC
32
- when :RFCTYPE_TIME
33
- # TODO: See whether we can optimize this
34
- timec = read_chars(metadata)
35
- return Time.parse("#{timec[0..1]}:#{timec[2..3]}:#{timec[4..5]}")
36
- when :RFCTYPE_BYTE
37
- return read_chars(metadata)
38
- when :RFCTYPE_TABLE
39
- new_handle = NWRFCLib::RFCDataContainer.new
40
- rc = NWRFCLib.get_table(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
41
- NWRFC.check_error(@error) if rc > 0
42
- # CAVEAT: Other calls using the handle require "handle" field
43
- # of the RFC_DATA_CONTAINER struct for some reason.
44
- new_handle = new_handle[:handle]
45
- value = Table.new(new_handle)
46
- when :RFCTYPE_NUM
47
- return read_chars(metadata).to_i
48
- when :RFCTYPE_FLOAT
49
- double = FFI::MemoryPointer.new :double
50
- rc = NWRFCLib.get_float(@handle, member.cU, double, @error)
51
- NWRFC.check_error(@error) if rc > 0
52
- return double.get_double(0)
53
- when :RFCTYPE_INT
54
- int = FFI::MemoryPointer.new :int
55
- rc = NWRFCLib.get_int(@handle, member.cU, int, @error)
56
- NWRFC.check_error(@error) if rc > 0
57
- return int.get_int(0)
58
- when :RFCTYPE_INT2
59
- short = FFI::MemoryPointer.new :short
60
- rc = NWRFCLib.get_int2(@handle, member.cU, short, @error)
61
- NWRFC.check_error(@error) if rc > 0
62
- return short.get_short(0)
63
- when :RFCTYPE_INT1
64
- int1 = FFI::MemoryPointer.new :uint8
65
- rc = NWRFCLib.get_int1(@handle, member.cU, int1, @error)
66
- NWRFC.check_error(@error) if rc > 0
67
- return int1.get_uint8(0)
68
- when :RFCTYPE_NULL
69
- raise "Unsupported type RFCTYPE_NULL" #You should never run into this
70
- when :RFCTYPE_STRUCTURE
71
- new_handle = NWRFCLib::RFCDataContainer.new
72
- rc = NWRFCLib.get_structure(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
73
- NWRFC.check_error(@error) if rc > 0
74
- new_handle = new_handle[:handle]
75
- value = Structure.new(new_handle)
76
- when :RFCTYPE_DECF16
77
- double = FFI::MemoryPointer.new :double
78
- rc = NWRFCLib.get_dec_f16(@handle, member.cU, double, @error)
79
- NWRFC.check_error(@error) if rc > 0
80
- return double.get_double(0)
81
- when :RFCTYPE_DECF34
82
- double = FFI::MemoryPointer.new :double, 2
83
- rc = NWRFCLib.get_dec_f34(@handle, member.cU, double, @error)
84
- NWRFC.check_error(@error) if rc > 0
85
- return double.get_double(0)
86
- when :RFCTYPE_XMLDATA
87
- raise "Unsupported type RFCTYPE_XMLDATA (no longer used)" #You should never run into this
88
- when :RFCTYPE_STRING
89
- return read_string(metadata)
90
- when :RFCTYPE_XSTRING
91
- size = FFI::MemoryPointer.new(:uint)
92
- rc = NWRFCLib.get_string_length(@handle, metadata[:name].cU, size, @error)
93
- NWRFC.check_error(@error) if rc > 0
94
- buf_len = size.read_uint
95
- sbuf = FFI::MemoryPointer.new :uchar, buf_len
96
- rc = NWRFCLib.get_x_string(@handle, metadata[:name].cU, sbuf, buf_len, size, @error)
97
- NWRFC.check_error(@error) if rc > 0
98
- return sbuf.read_string(sbuf.size)
99
- else
100
- raise "Illegal member type #{metadata[:type]}"
32
+
33
+ when :RFCTYPE_BCD
34
+ size = metadata[:nucLength] + (metadata[:decimals] || 0)
35
+ buf = FFI::MemoryPointer.new(:uchar, size*2)
36
+ rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, buf, size, @error.to_ptr)
37
+ NWRFC.check_error(@error) if rc > 0
38
+ return buf.get_bytes(0, size*2).uC.to_f
39
+ #size = metadata[:ucLength]
40
+ #cb = FFI::MemoryPointer.new :char, size * 2
41
+ #rc = NWRFCLib.get_chars(@handle, metadata[:name].cU, cb, size * 2, @error.to_ptr)
42
+ #NWRFC.check_error(@error) if rc > 0
43
+ #cb.read_string(size).uC
44
+
45
+ when :RFCTYPE_TIME
46
+ # TODO: See whether we can optimize this
47
+ timec = read_chars(metadata)
48
+ return Time.parse("#{timec[0..1]}:#{timec[2..3]}:#{timec[4..5]}")
49
+
50
+ when :RFCTYPE_BYTE
51
+ size = metadata[:ucLength]
52
+ buf = FFI::MemoryPointer.new(:uchar, size)
53
+ rc = NWRFCLib.get_bytes(@handle, metadata[:name].cU, buf, size, @error.to_ptr)
54
+ NWRFC.check_error(@error) if rc > 0
55
+ return buf.get_bytes(0, size)
56
+
57
+ when :RFCTYPE_TABLE
58
+ # TODO Cache instances of table members and return those where available
59
+ new_handle = NWRFCLib::RFCDataContainer.new
60
+ rc = NWRFCLib.get_table(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
61
+ NWRFC.check_error(@error) if rc > 0
62
+ # CAVEAT: Other calls using the handle require "handle" field
63
+ # of the RFC_DATA_CONTAINER struct for some reason.
64
+ new_handle = new_handle[:handle]
65
+ return Table.new(new_handle)
66
+
67
+ when :RFCTYPE_NUM
68
+ return read_chars(metadata)
69
+
70
+ when :RFCTYPE_FLOAT
71
+ double = FFI::MemoryPointer.new :double
72
+ rc = NWRFCLib.get_float(@handle, member.cU, double, @error)
73
+ NWRFC.check_error(@error) if rc > 0
74
+ return double.get_double(0)
75
+
76
+ when :RFCTYPE_INT
77
+ int = FFI::MemoryPointer.new :int
78
+ rc = NWRFCLib.get_int(@handle, member.cU, int, @error)
79
+ NWRFC.check_error(@error) if rc > 0
80
+ return int.get_int(0)
81
+
82
+ when :RFCTYPE_INT2
83
+ short = FFI::MemoryPointer.new :short
84
+ rc = NWRFCLib.get_int2(@handle, member.cU, short, @error)
85
+ NWRFC.check_error(@error) if rc > 0
86
+ return short.get_short(0)
87
+
88
+ when :RFCTYPE_INT1
89
+ int1 = FFI::MemoryPointer.new :uint8
90
+ rc = NWRFCLib.get_int1(@handle, member.cU, int1, @error)
91
+ NWRFC.check_error(@error) if rc > 0
92
+ return int1.get_uint8(0)
93
+
94
+ when :RFCTYPE_NULL
95
+ raise "Unsupported type RFCTYPE_NULL" #You should never run into this
96
+
97
+ when :RFCTYPE_STRUCTURE
98
+ # TODO Cache instances of structure members and return those where available
99
+ new_handle = NWRFCLib::RFCDataContainer.new
100
+ rc = NWRFCLib.get_structure(@handle, member.cU, new_handle.to_ptr, @error.to_ptr)
101
+ NWRFC.check_error(@error) if rc > 0
102
+ new_handle = new_handle[:handle]
103
+ return Structure.new(new_handle)
104
+
105
+ when :RFCTYPE_DECF16
106
+ double = FFI::MemoryPointer.new :double
107
+ rc = NWRFCLib.get_dec_f16(@handle, member.cU, double, @error)
108
+ NWRFC.check_error(@error) if rc > 0
109
+ return double.get_double(0)
110
+
111
+ when :RFCTYPE_DECF34
112
+ double = FFI::MemoryPointer.new :double, 2
113
+ rc = NWRFCLib.get_dec_f34(@handle, member.cU, double, @error)
114
+ NWRFC.check_error(@error) if rc > 0
115
+ return double.get_double(0)
116
+
117
+ when :RFCTYPE_XMLDATA
118
+ raise "Unsupported type RFCTYPE_XMLDATA (no longer used)" #You should never run into this
119
+
120
+ when :RFCTYPE_STRING
121
+ return read_string(metadata)
122
+
123
+ when :RFCTYPE_XSTRING
124
+ size = FFI::MemoryPointer.new(:uint)
125
+ rc = NWRFCLib.get_string_length(@handle, metadata[:name].cU, size, @error)
126
+ NWRFC.check_error(@error) if rc > 0
127
+ buf_len = size.read_uint
128
+ sbuf = FFI::MemoryPointer.new :uchar, buf_len
129
+ rc = NWRFCLib.get_x_string(@handle, metadata[:name].cU, sbuf, buf_len, size, @error)
130
+ NWRFC.check_error(@error) if rc > 0
131
+ return sbuf.read_string(sbuf.size)
132
+
133
+ else
134
+ raise "Illegal member type #{metadata[:type]}"
101
135
  end
102
- NWRFC.check_error(@error)
103
- value
136
+
104
137
  end
105
138
 
139
+ #--
140
+ # VALUE STORAGE
141
+ #++
142
+
143
+ # Set value on a data container (structure, function instance or table)
106
144
  def []=(element, value)
107
145
  member = element.to_s.upcase
108
146
  metadata = member_metadata(element)
109
147
  case metadata[:type]
110
- when :RFCTYPE_CHAR
111
- NWRFCLib.set_chars(@handle, member.cU, value.cU, value.length, @error.to_ptr)
112
- when :RFCTYPE_DATE
113
- value = value_to_date(value)
114
- NWRFCLib.set_date(@handle, member.cU, value.cU, @error.to_ptr)
115
- when :RFCTYPE_BCD
116
- when :RFCTYPE_TIME
117
- value = value_to_time(value)
118
- NWRFCLib.set_time(@handle, member.cU, value.cU, @error.to_ptr)
119
- when :RFCTYPE_BYTE
120
- when :RFCTYPE_TABLE
121
- when :RFCTYPE_NUM
122
- when :RFCTYPE_FLOAT
123
- NWRFCLib.set_float(@handle, member.cU, value.to_f, @error.to_ptr)
124
- #NWRFCLib.set_chars(@handle, member.cU, value.to_s.cU, value.to_s.length, @error.to_ptr)
125
- when :RFCTYPE_INT
126
- NWRFCLib.set_int(@handle, member.cU, value.to_i, @error.to_ptr)
127
- when :RFCTYPE_INT2
128
- NWRFCLib.set_int2(@handle, member.cU, value.to_i, @error.to_ptr)
129
- when :RFCTYPE_INT1
130
- NWRFCLib.set_int1(@handle, member.cU, value.to_i, @error.to_ptr)
131
- when :RFCTYPE_NULL
132
- raise "Unsupported type RFCTYPE_NULL" #You should never run into this
133
- when :RFCTYPE_STRUCTURE
134
- when :RFCTYPE_DECF16
135
- raise "#{@members[:type]}: decfloat16 not supported yet"
136
- double = NWRFCLib::RFC_DECF16.new #FFI::MemoryPointer.new :double
137
- double[:align] = value.to_f
138
- #double.put_double(0, value.to_f)
139
- #double = FFI::Pointer.new 4
140
- NWRFCLib.set_dec_f16(@handle, member.cU, double.pointer, @error)
141
- when :RFCTYPE_DECF34
142
- raise "#{@members[:type]}: decfloat34 not supported yet"
143
- # double = FFI::MemoryPointer.new :double, 2
144
- # double.put_double(0, value.to_f)
145
- double = NWRFCLib::RFC_DECF34.new #FFI::MemoryPointer.new :double
146
- double[:align] = value.to_f
147
- NWRFCLib.set_dec_f34(@handle, member.cU, double, @error)
148
- when :RFCTYPE_XMLDATA
149
- raise "Unsupported type RFCTYPE_XMLDATA (no longer used)" #You should never run into this
150
- when :RFCTYPE_STRING
151
- when :RFCTYPE_XSTRING
152
- m = FFI::MemoryPointer.new value.size
153
- m.put_bytes 0, value
154
- NWRFCLib.set_x_string(@handle, member.cU, m, value.size, @error)
155
- else
156
- raise "Illegal member type #{@members[:type]}"
148
+
149
+ when :RFCTYPE_CHAR
150
+ value = value.to_s
151
+ NWRFCLib.set_chars(@handle, member.cU, value.cU, value.length, @error.to_ptr)
152
+
153
+ when :RFCTYPE_DATE
154
+ value = value_to_date(value)
155
+ NWRFCLib.set_date(@handle, member.cU, value.cU, @error.to_ptr)
156
+
157
+ when :RFCTYPE_BCD
158
+ stval = value.to_s.cU
159
+ m = FFI::MemoryPointer.from_string stval
160
+ NWRFCLib.set_string(@handle, member.cU, m, value.to_s.size, @error)
161
+
162
+ when :RFCTYPE_TIME
163
+ value = value_to_time(value)
164
+ NWRFCLib.set_time(@handle, member.cU, value.cU, @error.to_ptr)
165
+
166
+ when :RFCTYPE_BYTE
167
+ m = FFI::MemoryPointer.from_string value.to_s
168
+ NWRFCLib.set_bytes(@handle, member.cU, m, value.to_s.size, @error.to_ptr)
169
+
170
+ when :RFCTYPE_TABLE
171
+ raise "Value must be of type table" unless value.class == NWRFC::Table
172
+ NWRFCLib.set_table(@handle, member.cU, value.handle, @error)
173
+
174
+ when :RFCTYPE_NUM
175
+ value = value.to_s
176
+ NWRFCLib.set_num(@handle, member.cU, value.cU, value.length, @error.to_ptr)
177
+
178
+ when :RFCTYPE_FLOAT
179
+ NWRFCLib.set_float(@handle, member.cU, value.to_f, @error.to_ptr)
180
+
181
+ when :RFCTYPE_INT
182
+ NWRFCLib.set_int(@handle, member.cU, value.to_i, @error.to_ptr)
183
+
184
+ when :RFCTYPE_INT2
185
+ NWRFCLib.set_int2(@handle, member.cU, value.to_i, @error.to_ptr)
186
+
187
+ when :RFCTYPE_INT1
188
+ NWRFCLib.set_int1(@handle, member.cU, value.to_i, @error.to_ptr)
189
+
190
+ when :RFCTYPE_NULL
191
+ raise "Unsupported type RFCTYPE_NULL" #You should never run into this
192
+
193
+ when :RFCTYPE_STRUCTURE
194
+ raise "Value must be of type table" unless value.class == NWRFC::Structure
195
+ NWRFCLib.set_structure(@handle, member.cU, value.handle, @error)
196
+
197
+ when :RFCTYPE_DECF16
198
+ raise "#{@members[:type]}: decfloat16 not supported yet"
199
+ double = NWRFCLib::RFC_DECF16.new #FFI::MemoryPointer.new :double
200
+ double[:align] = value.to_f
201
+ #double.put_double(0, value.to_f)
202
+ #double = FFI::Pointer.new 4
203
+ NWRFCLib.set_dec_f16(@handle, member.cU, double.pointer, @error)
204
+
205
+ when :RFCTYPE_DECF34
206
+ raise "#{@members[:type]}: decfloat34 not supported yet"
207
+ # double = FFI::MemoryPointer.new :double, 2
208
+ # double.put_double(0, value.to_f)
209
+ double = NWRFCLib::RFC_DECF34.new #FFI::MemoryPointer.new :double
210
+ double[:align] = value.to_f
211
+ NWRFCLib.set_dec_f34(@handle, member.cU, double, @error)
212
+
213
+ when :RFCTYPE_XMLDATA
214
+ raise "Unsupported type RFCTYPE_XMLDATA (no longer used)" #You should never run into this
215
+
216
+ when :RFCTYPE_STRING
217
+ stval = value.cU
218
+ m = FFI::MemoryPointer.from_string stval
219
+ NWRFCLib.set_string(@handle, member.cU, m, value.size, @error)
220
+
221
+ when :RFCTYPE_XSTRING
222
+ m = FFI::MemoryPointer.new value.size
223
+ m.put_bytes 0, value
224
+ NWRFCLib.set_x_string(@handle, member.cU, m, value.size, @error)
225
+
226
+ else
227
+ raise "Illegal member type #{@members[:type]}"
157
228
  end
158
229
  NWRFC.check_error(@error)
159
230
  end
160
231
 
161
- def value_to_time(value)
162
- return value.strftime(NW_TIME_FORMAT) if value.respond_to? :strftime
163
- value.to_s
164
- end
165
-
232
+ # Return value as a SAP-formatted date ("YYYYMMDD"). Force value to fit into 8 chars by
233
+ # truncating or padding with spaces
166
234
  def value_to_date(value)
167
235
  return value.strftime(NW_DATE_FORMAT) if value.respond_to? :strftime
168
236
  # Force the resulting string into 8 characters otherwise
@@ -172,6 +240,8 @@ module NWRFC
172
240
  value
173
241
  end
174
242
 
243
+ # Return value as a SAP-formatted time ("HHMMSS"). Force value to fit into 6 chars by
244
+ # truncating or padding with spaces
175
245
  def value_to_time(value)
176
246
  return value.strftime(NW_TIME_FORMAT) if value.respond_to? :strftime
177
247
  # Force the resulting string into 6 characters otherwise
@@ -183,6 +253,7 @@ module NWRFC
183
253
 
184
254
  # Get the metadata of a member (function, structure or table)
185
255
  def member_metadata(member_name)
256
+ # TODO: Cache metadata definitions; will it be quicker than making a hash of metadata for a given member each time?
186
257
  member = member_name.to_s.upcase
187
258
  if self.class == NWRFC::FunctionCall
188
259
  fpar = NWRFCLib::RFCFuncParam.new
@@ -202,12 +273,12 @@ module NWRFC
202
273
  # and a type field
203
274
  def member_to_hash(member)
204
275
  {
205
- :name => member[:name].get_str,
206
- :type => NWRFCLib::RFC_TYPE[member[:type]],
207
- :nucLength => member[:nucLength],
208
- :ucLength => member[:ucLength],
209
- :decimals => member[:decimals],
210
- :typeDescHandle => member[:typeDescHandle]
276
+ :name => member[:name].get_str,
277
+ :type => NWRFCLib::RFC_TYPE[member[:type]],
278
+ :nucLength => member[:nucLength],
279
+ :ucLength => member[:ucLength],
280
+ :decimals => member[:decimals],
281
+ :typeDescHandle => member[:typeDescHandle]
211
282
  }
212
283
  end
213
284
 
@@ -0,0 +1,25 @@
1
+ module NWRFC
2
+
3
+ class NWError < Exception
4
+
5
+ attr_reader :code, :group, :message, :class, :type, :number
6
+
7
+ # Instantiate Error object with a handle to an FFI::MemoryPointer
8
+ # to an NWRFCLib::RFCError object. The error object is analyzed so that
9
+ # when the caller intercepts it with Rescue, all the error details are
10
+ # available
11
+ def initialize(error)
12
+ @code = NWRFCLib::RFC_RC[error[:code]]
13
+ @group = NWRFCLib::RFC_ERROR_GROUP[error[:group]]
14
+ @message = error[:message].get_str
15
+ @type = error[:abapMsgType].get_str
16
+ @number = error[:abapMsgNumber].get_str
17
+ end
18
+
19
+ def inspect
20
+ "#{@message} (code #{@code}, group #{@group}, type #{@type}, number #{@number})"
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -143,12 +143,16 @@ module NWRFCLib
143
143
  :RFCTYPE_INT2 , 9,
144
144
  :RFCTYPE_INT1 , 10,
145
145
  :RFCTYPE_NULL , 14,
146
+ :RFCTYPE_ABAPOBJECT, 16,
146
147
  :RFCTYPE_STRUCTURE , 17,
147
148
  :RFCTYPE_DECF16 , 23,
148
149
  :RFCTYPE_DECF34 , 24,
149
150
  :RFCTYPE_XMLDATA , 28,
150
151
  :RFCTYPE_STRING , 29,
151
- :RFCTYPE_XSTRING , 30
152
+ :RFCTYPE_XSTRING , 30,
153
+ :RFCTYPE_BOX, 31,
154
+ :RFCTYPE_GENERIC_BOX, 32,
155
+ :_RFCTYPE_max_value
152
156
  )
153
157
 
154
158
  # Connection parameter wrapper (struct RFC_CONNECTION_PARAMETER in sapnwrfc.h)
@@ -267,6 +271,10 @@ module NWRFCLib
267
271
  # yet used in our NWRFC library, so calling them may not work. For best results, consult sapnwrfc.h from
268
272
  # the SDK
269
273
  #############################################################################################################
274
+ # Callback for function server (function implementation)
275
+ callback :funcimpl, [:pointer, :pointer, :pointer], :int
276
+
277
+ # Function mappings
270
278
  [
271
279
  [:add_exception, :RfcAddException, [:pointer, :pointer, :pointer], :int],
272
280
  [:add_function_desc, :RfcAddFunctionDesc, [:pointer, :pointer, :pointer], :int],
@@ -343,7 +351,7 @@ module NWRFCLib
343
351
  [:insert_new_row, :RfcInsertNewRow, [:pointer, :pointer], :pointer],
344
352
  [:insert_row, :RfcInsertRow, [:pointer, :pointer, :pointer], :int],
345
353
  [:install_generic_server_function, :RfcInstallGenericServerFunction, [:pointer, :pointer, :pointer], :int],
346
- [:install_server_function, :RfcInstallServerFunction, [:pointer, :pointer, :pointer, :pointer], :int],
354
+ [:install_server_function, :RfcInstallServerFunction, [:pointer, :pointer, :funcimpl, :pointer], :int],
347
355
  [:install_transaction_handlers, :RfcInstallTransactionHandlers, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int],
348
356
  [:invoke, :RfcInvoke, [:pointer, :pointer, :pointer], :int],
349
357
  [:invoke_in_transaction, :RfcInvokeInTransaction, [:pointer, :pointer, :pointer], :int],
@@ -0,0 +1,36 @@
1
+ module NWRFC
2
+
3
+ # Implementation of a server to host RFC functions to be called from an ABAP system
4
+ # or from another RFC implementation
5
+ class Server
6
+
7
+ TIMEOUT = 200
8
+
9
+ # Register a server with the given gateway and program ID
10
+ def initialize(params)
11
+ raise "Rarameters must be a Hash" unless params.instance_of? Hash
12
+ @rparams = NWRFC.make_conn_params(params)
13
+ raise "Could not create valid pointer from parameters" unless @rparams.instance_of? FFI::MemoryPointer
14
+ @error = NWRFCLib::RFCError.new
15
+ @handle = NWRFCLib.register_server(@rparams, params.size, @error)
16
+ NWRFC.check_error(@error)
17
+ end
18
+
19
+ def serve(function, &block)
20
+ # Establish callback handler
21
+ callback = Proc.new do |connection, function_call, error|
22
+ fc =
23
+ debugger
24
+ yield(function_call)
25
+ end
26
+ rc = NWRFCLib.install_server_function(nil, function.desc, callback, @error)
27
+ NWRFC.check_error(@error) if rc > 0
28
+ # Server loop
29
+ while (rc==NWRFCLib::RFC_RC[:RFC_OK] || rc==NWRFCLib::RFC_RC[:RFC_RETRY] || rc==NWRFCLib::RFC_RC[:RFC_ABAP_EXCEPTION])
30
+ rc = NWRFCLib.listen_and_dispatch(@handle, TIMEOUT, @error)
31
+ end
32
+ end
33
+
34
+ end
35
+
36
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nwrfc
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
9
+ - 2
10
+ version: 0.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Martin Ceronio
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-02-20 00:00:00 Z
18
+ date: 2012-02-22 00:00:00 Z
19
19
  dependencies: []
20
20
 
21
21
  description: SAP Netweaver RFC Library Wrapper using Ruby-FFI
@@ -30,10 +30,10 @@ files:
30
30
  - README.rdoc
31
31
  - Rakefile
32
32
  - lib/nwrfc.rb
33
+ - lib/nwrfc/server.rb
33
34
  - lib/nwrfc/datacontainer.rb
35
+ - lib/nwrfc/nwerror.rb
34
36
  - lib/nwrfc/nwrfclib.rb
35
- - lib/nwrfc/dev_rfc.trc
36
- - lib/dev_rfc.trc
37
37
  homepage: http://rubygems.org/gems/nwrfc
38
38
  licenses: []
39
39
 
data/lib/dev_rfc.trc DELETED
@@ -1,140 +0,0 @@
1
-
2
- **** Trace file opened at 2012-02-15, 13:50:29 SAST
3
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
4
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
5
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
6
-
7
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
8
-
9
- **** Trace file opened at 2012-02-15, 13:51:02 SAST
10
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
11
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
12
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
13
-
14
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
15
-
16
- **** Trace file opened at 2012-02-15, 14:07:53 SAST
17
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
18
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
19
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
20
-
21
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
22
-
23
- **** Trace file opened at 2012-02-15, 14:08:23 SAST
24
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
25
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
26
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
27
-
28
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
29
-
30
- **** Trace file opened at 2012-02-15, 14:09:49 SAST
31
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
32
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
33
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
34
-
35
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
36
-
37
- **** Trace file opened at 2012-02-15, 14:40:33 SAST
38
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb1
39
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
40
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
41
-
42
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
43
-
44
- **** Trace file opened at 2012-02-15, 14:42:34 SAST
45
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
46
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
47
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
48
-
49
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
50
-
51
- **** Trace file opened at 2012-02-15, 14:50:05 SAST
52
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
53
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
54
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
55
-
56
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
57
-
58
- **** Trace file opened at 2012-02-15, 15:19:16 SAST
59
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
60
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
61
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
62
-
63
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
64
-
65
- **** Trace file opened at 2012-02-15, 15:19:42 SAST
66
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
67
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
68
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
69
-
70
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
71
-
72
- **** Trace file opened at 2012-02-15, 15:20:37 SAST
73
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
74
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
75
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
76
-
77
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
78
-
79
- **** Trace file opened at 2012-02-15, 15:21:59 SAST
80
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
81
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
82
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
83
-
84
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
85
-
86
- **** Trace file opened at 2012-02-15, 15:23:05 SAST
87
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
88
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
89
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
90
-
91
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
92
-
93
- **** Trace file opened at 2012-02-15, 15:25:42 SAST
94
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
95
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
96
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
97
-
98
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
99
-
100
- **** Trace file opened at 2012-02-15, 15:26:25 SAST
101
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
102
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
103
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
104
-
105
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
106
-
107
- **** Trace file opened at 2012-02-15, 22:23:25 SAST
108
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
109
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
110
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
111
-
112
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
113
-
114
- **** Trace file opened at 2012-02-15, 22:23:59 SAST
115
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
116
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
117
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
118
-
119
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
120
-
121
- **** Trace file opened at 2012-02-15, 22:27:38 SAST
122
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
123
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
124
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
125
-
126
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
127
-
128
- **** Trace file opened at 2012-02-15, 22:28:08 SAST
129
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
130
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
131
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
132
-
133
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
134
-
135
- **** Trace file opened at 2012-02-17, 12:38:41 SAST
136
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
137
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
138
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
139
-
140
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
@@ -1,28 +0,0 @@
1
-
2
- **** Trace file opened at 2012-02-17, 14:29:25 SAST
3
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib/nwrfc, Program: irb
4
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
5
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
6
-
7
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
8
-
9
- **** Trace file opened at 2012-02-17, 14:35:20 SAST
10
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib/nwrfc, Program: irb
11
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
12
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
13
-
14
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
15
-
16
- **** Trace file opened at 2012-02-17, 14:50:20 SAST
17
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib/nwrfc, Program: irb
18
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
19
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
20
-
21
- ERROR pfuuid_init returns error -> UUIDs cannot be generated
22
-
23
- **** Trace file opened at 2012-02-17, 15:04:13 SAST
24
- RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib/nwrfc, Program: irb
25
- Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
26
- Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
27
-
28
- ERROR pfuuid_init returns error -> UUIDs cannot be generated