Skip to content

Commit

Permalink
bugfixes and simplifications; tests passing
Browse files Browse the repository at this point in the history
  • Loading branch information
thomvet committed Nov 20, 2024
1 parent ad1600f commit a9ec4fd
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 62 deletions.
4 changes: 1 addition & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ Distributed = "1.6"
DocStringExtensions = "0.9"
OffsetArrays = "1"
Pkg = "1.6"
Printf = "1.11.0"
Random = "1.6"
SafeTestsets = "0.1.0"
Test = "1.6"
Expand All @@ -31,10 +30,9 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Aqua", "Base64", "Distributed", "Pkg", "Printf", "Random", "SafeTestsets", "Test"]
test = ["Aqua", "Base64", "Distributed", "Pkg", "Random", "SafeTestsets", "Test"]
162 changes: 116 additions & 46 deletions test/client_callmethod.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,150 @@ Distributed.@everywhere begin
using Test
using Pkg
using Pkg.BinaryPlatforms
end

Distributed.@spawnat Distributed.workers()[end] begin
#configure server
server = JUA_Server()
retval0 = JUA_ServerConfig_setMinimalCustomBuffer(JUA_ServerConfig(server),
4842, C_NULL, 0, 0)
@test retval0 == UA_STATUSCODE_GOOD

#add method node
#create methods that will be used later
function simple_one_in_one_out(name)
assembledstring = "Hello "*name*"."
return assembledstring
end

function simple_two_in_one_out(name, adjective)
assembledstring = "Hello "*name*", you are "*adjective*"."
return assembledstring
end

function simple_one_in_two_out(name)
out1 = "Hello "*name*"."
out2 = reverse(name)*" is "*name*" reversed."
return (out1, out2)
end

function simple_two_in_two_out(name, adjective)
out1 = "Hello "*name*", you are "*adjective*"."
out2 = adjective*" is the adjective."
return (out1, out2)
end

function simple_two_in_two_out_mixed_type(name, number)
out1 = "Hello "*name*"."
out2 = number*number
return (out1, out2)
end

#lower level without abstraction
function two_in_one_out(server, sessionId, sessionHandle, methodId, methodContext, objectId,
function c1(server, sessionId, sessionHandle, methodId, methodContext, objectId,
objectContext, inputSize, input, outputSize, output)
arr_input = UA_Array(input, Int64(inputSize))
arr_output = UA_Array(output, Int64(outputSize))
input_julia = Open62541.__get_juliavalues_from_variant.(arr_input, Any)
output_julia = simple_one_in_one_out(input_julia...)
if !isa(output_julia, Tuple)
output_julia = (output_julia,)
end
for i in 1:outputSize
j = JUA_Variant(output_julia[i])
UA_Variant_copy(Open62541.Jpointer(j), arr_output[i])
end
return UA_STATUSCODE_GOOD
end
function c2(server, sessionId, sessionHandle, methodId, methodContext, objectId,
objectContext, inputSize, input, outputSize, output)
arr_input = UA_Array(input, Int64(inputSize))
arr_output = UA_Array(output, Int64(outputSize))
input_julia = Open62541.__get_juliavalues_from_variant.(arr_input, Any)
output_julia = simple_two_in_one_out(input_julia...)
if !isa(output_julia, Tuple)
output_julia = (output_julia,)
end
for i in 1:outputSize
j = JUA_Variant(output_julia[i])
UA_Variant_copy(Open62541.Jpointer(j), arr_output[i])
end
return UA_STATUSCODE_GOOD
end
function c3(server, sessionId, sessionHandle, methodId, methodContext, objectId,
objectContext, inputSize, input, outputSize, output)
arr_input = UA_Array(input, Int64(inputSize))
arr_output = UA_Array(output, Int64(outputSize))
input_julia = Open62541.__get_juliavalues_from_variant.(arr_input, Any)
output_julia = simple_one_in_two_out(input_julia...)
if !isa(output_julia, Tuple)
output_julia = (output_julia,)
end
for i in 1:outputSize
j = JUA_Variant(output_julia[i])
UA_Variant_copy(Open62541.Jpointer(j), arr_output[i])
end
return UA_STATUSCODE_GOOD
end
function c4(server, sessionId, sessionHandle, methodId, methodContext, objectId,
objectContext, inputSize, input, outputSize, output)
arr = UA_Array(input, Int64(inputSize))
strings = Open62541.__get_juliavalues_from_variant.(arr, Any)
assembledstring = "Hello "*strings[1]*", you are "*strings[2]
j = JUA_Variant(assembledstring)
UA_Variant_copy(Open62541.Jpointer(j), output)
arr_input = UA_Array(input, Int64(inputSize))
arr_output = UA_Array(output, Int64(outputSize))
input_julia = Open62541.__get_juliavalues_from_variant.(arr_input, Any)
output_julia = simple_two_in_two_out(input_julia...)
if !isa(output_julia, Tuple)
output_julia = (output_julia,)
end
for i in 1:outputSize
j = JUA_Variant(output_julia[i])
UA_Variant_copy(Open62541.Jpointer(j), arr_output[i])
end
return UA_STATUSCODE_GOOD
end

function c5(server, sessionId, sessionHandle, methodId, methodContext, objectId,
objectContext, inputSize, input, outputSize, output)
arr_input = UA_Array(input, Int64(inputSize))
arr_output = UA_Array(output, Int64(outputSize))
input_julia = Open62541.__get_juliavalues_from_variant.(arr_input, Any)
output_julia = simple_two_in_two_out_mixed_type(input_julia...)
if !isa(output_julia, Tuple)
output_julia = (output_julia,)
end
for i in 1:outputSize
j = JUA_Variant(output_julia[i])
UA_Variant_copy(Open62541.Jpointer(j), arr_output[i])
end
return UA_STATUSCODE_GOOD
end
end

Distributed.@spawnat Distributed.workers()[end] begin
#prepare method callbacks
@static if !Sys.isapple() || platform_key_abi().tags["arch"] != "aarch64"
w1 = UA_MethodCallback_wrap(simple_one_in_one_out)
w2 = UA_MethodCallback_wrap(simple_two_in_one_out)
w3 = UA_MethodCallback_wrap(simple_one_in_two_out)
w4 = UA_MethodCallback_wrap(simple_two_in_two_out)
w5 = UA_MethodCallback_wrap(simple_two_in_two_out_mixed_type)
m1 = UA_MethodCallback_generate(w1)
m2 = UA_MethodCallback_generate(w2)
m3 = UA_MethodCallback_generate(w3)
m4 = UA_MethodCallback_generate(w4)
m5 = UA_MethodCallback_generate(w5)
else #we are on Apple Silicon and can't use a closure in @cfunction, have to do MUCH more work.
m1 = @cfunction(c1, UA_StatusCode,
(Ptr{UA_Server}, Ptr{UA_NodeId}, Ptr{Cvoid},
Ptr{UA_NodeId}, Ptr{Cvoid}, Ptr{UA_NodeId}, Ptr{Cvoid},
Csize_t, Ptr{UA_Variant}, Csize_t, Ptr{UA_Variant}))
m2 = @cfunction(c2, UA_StatusCode,
(Ptr{UA_Server}, Ptr{UA_NodeId}, Ptr{Cvoid},
Ptr{UA_NodeId}, Ptr{Cvoid}, Ptr{UA_NodeId}, Ptr{Cvoid},
Csize_t, Ptr{UA_Variant}, Csize_t, Ptr{UA_Variant}))
m3 = @cfunction(c3, UA_StatusCode,
(Ptr{UA_Server}, Ptr{UA_NodeId}, Ptr{Cvoid},
Ptr{UA_NodeId}, Ptr{Cvoid}, Ptr{UA_NodeId}, Ptr{Cvoid},
Csize_t, Ptr{UA_Variant}, Csize_t, Ptr{UA_Variant}))
m4 = @cfunction(c4, UA_StatusCode,
(Ptr{UA_Server}, Ptr{UA_NodeId}, Ptr{Cvoid},
Ptr{UA_NodeId}, Ptr{Cvoid}, Ptr{UA_NodeId}, Ptr{Cvoid},
Csize_t, Ptr{UA_Variant}, Csize_t, Ptr{UA_Variant}))
m5 = @cfunction(c5, UA_StatusCode,
(Ptr{UA_Server}, Ptr{UA_NodeId}, Ptr{Cvoid},
Ptr{UA_NodeId}, Ptr{Cvoid}, Ptr{UA_NodeId}, Ptr{Cvoid},
Csize_t, Ptr{UA_Variant}, Csize_t, Ptr{UA_Variant}))
end

#configure server
server = JUA_Server()
retval0 = JUA_ServerConfig_setMinimalCustomBuffer(JUA_ServerConfig(server),
4842, C_NULL, 0, 0)

#prepare method attributes
attr1 = JUA_MethodAttributes(description = "Simple One in One Out",
displayname = "Simple One in One Out",
Expand Down Expand Up @@ -89,25 +184,6 @@ Distributed.@spawnat Distributed.workers()[end] begin
parentnodeid = JUA_NodeId(0, UA_NS0ID_OBJECTSFOLDER)
parentreferencenodeid = JUA_NodeId(0, UA_NS0ID_HASCOMPONENT)

#prepare method callbacks
function wrap_method_by_architecture(method)
@static if !Sys.isapple() || platform_key_abi().tags["arch"] != "aarch64"
res = UA_MethodCallback_generate(method)
else #we are on Apple Silicon and can't use a closure in @cfunction, have to do more work.
res = @cfunction(method, UA_StatusCode,
(Ptr{UA_Server}, Ptr{UA_NodeId}, Ptr{Cvoid},
Ptr{UA_NodeId}, Ptr{Cvoid}, Ptr{UA_NodeId}, Ptr{Cvoid},
Csize_t, Ptr{UA_Variant}, Csize_t, Ptr{UA_Variant}))
end
return res
end

m1 = wrap_method_by_architecture(UA_MethodCallback_wrap(simple_one_in_one_out))
m2 = wrap_method_by_architecture(UA_MethodCallback_wrap(simple_two_in_one_out))
m3 = wrap_method_by_architecture(UA_MethodCallback_wrap(simple_one_in_two_out))
m4 = wrap_method_by_architecture(UA_MethodCallback_wrap(simple_two_in_two_out))
m5 = wrap_method_by_architecture(UA_MethodCallback_wrap(simple_two_in_two_out_mixed_type))

#prepare browsenames
browsename1 = JUA_QualifiedName(1, "Simple One in One Out")
browsename2 = JUA_QualifiedName(1, "Simple Two in One Out")
Expand Down Expand Up @@ -160,12 +236,6 @@ Distributed.@spawnat Distributed.workers()[end] begin
browsename5, attr5, m5, twoinputarg_mixed, twooutputarg_mixed,
JUA_NodeId(), JUA_NodeId())

@test retval1 == UA_STATUSCODE_GOOD
@test retval2 == UA_STATUSCODE_GOOD
@test retval3 == UA_STATUSCODE_GOOD
@test retval4 == UA_STATUSCODE_GOOD
@test retval5 == UA_STATUSCODE_GOOD

# Start up the server
Distributed.@spawnat Distributed.workers()[end] redirect_stderr() # Turn off all error messages
println("Starting up the server...")
Expand Down
29 changes: 16 additions & 13 deletions test/client_subscriptions.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#adapted from: https://github.com/open62541/open62541/blob/master/examples/client_subscription_loop.c
#the example (as of 2024-11-20) wants to create a subscription from within the
#callback, which is not allowed, see here:
#https://github.com/open62541/open62541/issues/5816
#Unmerged PR addressing this: https://github.com/open62541/open62541/pull/5905

using Distributed
using Printf
Distributed.addprocs(1) # Add a single worker process to run the server

Distributed.@everywhere begin
Expand All @@ -13,7 +16,7 @@ Distributed.@spawnat Distributed.workers()[end] begin
#configure the server
server = UA_Server_new()
retval = UA_ServerConfig_setMinimalCustomBuffer(UA_Server_getConfig(server),
4842, C_NULL, 0, 0)
4843, C_NULL, 0, 0)

# Start up the server
Distributed.@spawnat Distributed.workers()[end] redirect_stderr() # Turn off all error messages
Expand All @@ -27,25 +30,20 @@ config = UA_Client_getConfig(client)
UA_ClientConfig_setDefault(config)

#define callbacks
function handler_currentTimeChanged(client, subId, subContext, monId, monContext,
function handler_simple(client, subId, subContext, monId, monContext,
value)
if UA_Variant_hasScalarType(value.value, UA_TYPES_PTRS[UA_TYPES_DATETIME])
raw_date = unsafe_wrap(value.value)
dts = UA_DateTime_toStruct(raw_date)
push!(container, Printf.@sprintf("current date and time (UTC) is: %u-%u-%u %u:%u:%u.%03u\n",
dts.day, dts.month, dts.year, dts.hour, dts.min, dts.sec, dts.milliSec))
end
push!(container, rand()) #we just collect a bunch of random numbers here.
return nothing
end
handlercb = @cfunction(handler_currentTimeChanged, Cvoid, (Ptr{UA_Client}, UInt32, Ptr{Cvoid}, UInt32, Ptr{Cvoid}, UA_DataValue))
handlercb = @cfunction(handler_simple, Cvoid, (Ptr{UA_Client}, UInt32, Ptr{Cvoid}, UInt32, Ptr{Cvoid}, UA_DataValue))

#connect the client
max_duration = 90.0 # Maximum waiting time for server startup
sleep_time = 3.0 # Sleep time in seconds between each connection trial
let trial
trial = 0
while trial < max_duration / sleep_time
retval = UA_Client_connect(client, "opc.tcp://localhost:4842")
retval = UA_Client_connect(client, "opc.tcp://localhost:4843")
if retval == UA_STATUSCODE_GOOD
println("Connection established.")
break
Expand All @@ -68,8 +66,9 @@ monResponse = UA_Client_MonitoredItems_createDataChange(client, response.subscri
C_NULL, handlercb, C_NULL)

#now interrogate the thing
UA_Client_run_iterate(client, 1000)
container = String[]
container = Float64[] #need to initialize variable here, otherwise error (use in handler_currentTimeChanged(...))
UA_Client_run_iterate(client, 1000)
container = Float64[]
sleep(7)
UA_Client_run_iterate(client, 1000)
#see UA_CreateSubscriptionRequest_default();
Expand All @@ -80,3 +79,7 @@ UA_Client_run_iterate(client, 1000)

UA_Client_disconnect(client)
UA_Client_delete(client)

println("Ungracefully kill server process...")
Distributed.interrupt(Distributed.workers()[end])
Distributed.rmprocs(Distributed.workers()[end]; waitfor = 0)

0 comments on commit a9ec4fd

Please sign in to comment.