Skip to content

Commit 54805b2

Browse files
authored
Merge pull request #839 from JuliaRobotics/815/cgdfg_createsession
Optimizing the creation of a CGDFG.
2 parents 8fe55c8 + 8970f0c commit 54805b2

File tree

6 files changed

+56
-125
lines changed

6 files changed

+56
-125
lines changed

src/Neo4jDFG/entities/Neo4jDFG.jl

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,10 @@ mutable struct Neo4jDFG{T <: AbstractParams} <: AbstractDFG{T}
3333
!isValidLabel(robotId) && error("'$robotId' is not a valid Robot ID")
3434
!isValidLabel(sessionId) && error("'$sessionId' is not a valid Session ID")
3535

36-
# neo4jConnection = Neo4j.Connection(host, port=port, user=dbUser, password=dbPassword);
37-
# graph = Neo4j.getgraph(neo4jConnection)
38-
# neo4jInstance = Neo4jInstance(neo4jConnection, graph)
39-
4036
dfg = new{T}(neo4jInstance, userId, robotId, sessionId, description, addHistory, solverParams, blobStores)
4137
# Create the session if it doesn't already exist
4238
if createSessionNodes
4339
createDfgSessionIfNotExist(dfg)
44-
setUserData!(dfg, userData)
45-
setRobotData!(dfg, robotData)
46-
setSessionData!(dfg, sessionData)
47-
setDescription!(dfg, description)
4840
end
4941

5042
return dfg

src/Neo4jDFG/services/CGStructure.jl

Lines changed: 19 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ end
66

77
# Fastest way I can think to convert the data into a dict
88
#TODO: Probably should be made more efficient...definitely should be made more efficient
9-
# return ::Dict{String, Any}
109
function _convertNodeToDict(abstractNode::N) where N <: AbstractCGNode
1110
cp = deepcopy(abstractNode)
1211
data = length(cp.data) != 0 ? JSON2.write(cp.data) : "{}"
@@ -16,8 +15,6 @@ function _convertNodeToDict(abstractNode::N) where N <: AbstractCGNode
1615
return ser
1716
end
1817

19-
#TODO: Refactor, #HACK :D (but it works!)
20-
# return ::Session
2118
function _convertDictToSession(dict::Dict{String, Any})
2219
data = JSON2.read(String(base64decode(dict["data"])), Dict{Symbol, String})
2320
session = Session(
@@ -31,8 +28,7 @@ function _convertDictToSession(dict::Dict{String, Any})
3128
dict["lastUpdatedTimestamp"])
3229
return session
3330
end
34-
#TODO: Refactor, #HACK :D (but it works!)
35-
# returns ::Robot
31+
3632
function _convertDictToRobot(dict::Dict{String, Any})
3733
data = JSON2.read(String(base64decode(dict["data"])), Dict{Symbol, String})
3834
robot = Robot(
@@ -45,8 +41,7 @@ function _convertDictToRobot(dict::Dict{String, Any})
4541
dict["lastUpdatedTimestamp"])
4642
return robot
4743
end
48-
#TODO: Refactor, #HACK :D (but it works!)
49-
# returns ::User
44+
5045
function _convertDictToUser(dict::Dict{String, Any})
5146
data = JSON2.read(String(base64decode(dict["data"])), Dict{Symbol, String})
5247
user = User(
@@ -59,81 +54,33 @@ function _convertDictToUser(dict::Dict{String, Any})
5954
return user
6055
end
6156

62-
# returns ::User
63-
function createUser(dfg::Neo4jDFG, user::User)
64-
Symbol(dfg.userId) != user.id && error("DFG user ID must match user's ID")
65-
!isValidLabel(user) && error("Node cannot have an ID '$(user.id)'.")
66-
67-
props = _convertNodeToDict(user)
68-
# TODO: Switch to _queryNeo4j
69-
retNode = _createNode(dfg.neo4jInstance, ["USER", String(user.id)], props, nothing)
70-
return user
71-
end
72-
73-
# returns ::Robot
74-
function createRobot(dfg::Neo4jDFG, robot::Robot)
75-
Symbol(dfg.robotId) != robot.id && error("DFG robot ID must match robot's ID")
76-
Symbol(dfg.userId) != robot.userId && error("DFG user ID must match robot's user ID")
77-
!isValidLabel(robot) && error("Node cannot have an ID '$(robot.id)'.")
78-
79-
# Find the parent
80-
parents = _getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:USER:`$(dfg.userId)`)")
81-
length(parents) == 0 && error("Cannot find user '$(dfg.userId)'")
82-
length(parents) > 1 && error("Found multiple users '$(dfg.userId)'")
83-
84-
# Already exists?
85-
length(_getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:ROBOT:`$(dfg.userId)`:`$(robot.id)`)")) != 0 &&
86-
error("Robot '$(robot.id)' already exists for user '$(robot.userId)'")
87-
88-
props = _convertNodeToDict(robot)
89-
# TODO: Switch to _queryNeo4j
90-
retNode = _createNode(dfg.neo4jInstance, ["ROBOT", String(robot.userId), String(robot.id)], props, parents[1], :ROBOT)
91-
return robot
92-
end
93-
94-
# returns ::Session
95-
function createSession(dfg::Neo4jDFG, session::Session)
96-
Symbol(dfg.robotId) != session.robotId && error("DFG robot ID must match session's robot ID")
97-
Symbol(dfg.userId) != session.userId && error("DFG user ID must match session's->robot's->user ID")
98-
!isValidLabel(session) && error("Node cannot have an ID '$(session.id)'.")
99-
100-
# Find the parent
101-
parents = _getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:ROBOT:`$(dfg.robotId)`:`$(dfg.userId)`)")
102-
length(parents) == 0 && error("Cannot find robot '$(dfg.robotId)' for user '$(dfg.userId)'")
103-
length(parents) > 1 && error("Found multiple robots '$(dfg.robotId)' for user '$(dfg.userId)'")
104-
105-
# Already exists?
106-
length(_getNeoNodesFromCyphonQuery(dfg.neo4jInstance, "(node:SESSION:`$(session.userId)`:`$(session.robotId)`:`$(session.id)`)")) != 0 &&
107-
error("Session '$(session.id)' already exists for robot '$(session.robotId)' and user '$(session.userId)'")
108-
109-
props = _convertNodeToDict(session)
110-
# TODO: Switch to _queryNeo4j
111-
retNode = _createNode(dfg.neo4jInstance, ["SESSION", String(session.userId), String(session.robotId), String(session.id)], props, parents[1], :SESSION)
112-
return session
113-
end
114-
11557
"""
11658
$(SIGNATURES)
117-
Shortcut method to create the user, robot, and session if it doesn't already exist.
118-
119-
Notes
120-
- return `::Session`
59+
Efficient shortcut method to create the user, robot, and session if it doesn't already exist.
12160
"""
12261
function createDfgSessionIfNotExist(dfg::Neo4jDFG)
12362
strip(dfg.userId) == "" && error("User ID is not populated in DFG.")
12463
strip(dfg.robotId) == "" && error("Robot ID is not populated in DFG.")
12564
strip(dfg.sessionId) == "" && error("Session ID is not populated in DFG.")
65+
!isValidLabel(dfg.userId) && error("Node cannot have an ID '$(dfg.userId)'.")
66+
!isValidLabel(dfg.robotId) && error("Node cannot have an ID '$(dfg.robotId)'.")
67+
!isValidLabel(dfg.sessionId) && error("Node cannot have an ID '$(dfg.sessionId)'.")
68+
12669
user = User(Symbol(dfg.userId), dfg.userId, "Description for $(dfg.userId)", Dict{Symbol, String}())
12770
robot = Robot(Symbol(dfg.robotId), Symbol(dfg.userId), dfg.robotId, "Description for $(dfg.userId):$(dfg.robotId)", Dict{Symbol, String}())
12871
session = Session(Symbol(dfg.sessionId), Symbol(dfg.robotId), Symbol(dfg.userId), dfg.sessionId, dfg.description, Dict{Symbol, String}())
12972

130-
_getNodeCount(dfg.neo4jInstance, [dfg.userId, "USER"]) == 0 && createUser(dfg, user)
131-
_getNodeCount(dfg.neo4jInstance, [dfg.userId, dfg.robotId, "ROBOT"]) == 0 && createRobot(dfg, robot)
132-
if _getNodeCount(dfg.neo4jInstance, [dfg.userId, dfg.robotId, dfg.sessionId, "SESSION"]) == 0
133-
return createSession(dfg, session)
134-
else
135-
return getSession(dfg)
136-
end
73+
# NOTE that this doesn't get updated then, you need to use the set* (e.g. setSessionData) functions yourself.
74+
query = """
75+
MERGE (u:USER:`$(String(user.id))`) ON CREATE SET $(join(["u.$k = '$(v)'" for (k,v) in _convertNodeToDict(user)], ", "))\r\n
76+
MERGE (r:ROBOT:`$(String(user.id))`:`$(String(robot.id))`) ON CREATE SET $(join(["r.$k = '$(v)'" for (k,v) in _convertNodeToDict(robot)], ", "))\r\n
77+
MERGE (s:SESSION:`$(String(user.id))`:`$(String(robot.id))`:`$(String(session.id))`) ON CREATE SET $(join(["s.$k = '$(v)'" for (k,v) in _convertNodeToDict(session)], ", "))
78+
MERGE (u)-[:ROBOT]->(r)
79+
MERGE (r)-[:SESSION]->(s)
80+
"""
81+
82+
_queryNeo4j(dfg.neo4jInstance, query)
83+
return nothing
13784
end
13885

13986
"""
@@ -320,7 +267,7 @@ Notes
320267
- Returns `::Neo4jDFG `
321268
"""
322269
function copySession!(sourceDFG::Neo4jDFG, destDFG::Union{Nothing, <:Neo4jDFG})
323-
if destDFG == nothing
270+
if destDFG === nothing
324271
destDFG = _getDuplicatedEmptyDFG(sourceDFG)
325272
end
326273
_copyIntoGraph!(sourceDFG, destDFG, union(listVariables(sourceDFG), listFactors(sourceDFG)), true)

src/services/CustomPrinting.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,9 @@ function Base.show(io::IO, dfg::AbstractDFG)
191191
println(io, " Description: ", dfg.description)
192192
println(io, " Nr variables: ", length(ls(dfg)))
193193
println(io, " Nr factors: ",length(lsf(dfg)))
194-
println(io, " User Data: ", keys(dfg.userData))
195-
println(io, " Robot Data: ", keys(dfg.robotData))
196-
println(io, " Session Data: ", keys(dfg.sessionData))
194+
println(io, " User Data: ", keys(getUserData(dfg)))
195+
println(io, " Robot Data: ", keys(getRobotData(dfg)))
196+
println(io, " Session Data: ", keys(getSessionData(dfg)))
197197
end
198198

199199
Base.show(io::IO, ::MIME"text/plain", dfg::AbstractDFG) = show(io, dfg)

test/CGStructureTests.jl

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,21 @@ createDfgSessionIfNotExist(dfg)
1515
@test Symbol(dfg.userId) in map(u -> u.id, lsUsers(dfg))
1616
# Test that we can call it again.
1717
createDfgSessionIfNotExist(dfg)
18-
# And nuke it so we can try the longer functions.
19-
clearUser!!(dfg)
2018

2119
# User, robot, and session
2220
# TODO: Make easier ways to initialize these.
2321
user = User(Symbol(dfg.userId), "Bob Zack", "Description", Dict{Symbol, String}())
2422
robot = Robot(Symbol(dfg.robotId), user.id, "Test robot", "Description", Dict{Symbol, String}())
2523
session = Session(Symbol(dfg.sessionId), robot.id, user.id, "Test Session", "Description", Dict{Symbol, String}())
2624

27-
@test createUser(dfg, user) == user
28-
@test createRobot(dfg, robot) == robot
29-
@test createSession(dfg, session) == session
30-
@test map(s -> s.id, lsSessions(dfg)) == [Symbol(dfg.sessionId)]
31-
@test map(s -> s.id, lsRobots(dfg)) == [Symbol(dfg.robotId)]
32-
@test Symbol(dfg.userId) in map(u -> u.id, lsUsers(dfg))
33-
34-
# Test errors
35-
dfgError = deepcopy(dfg)
36-
# User/robot/session ID's can't start with numbers and can't have spaces.
37-
dfgError.userId = "1testNope"
38-
user = User(Symbol(dfgError.userId), "Bob Zack", "Description", Dict{Symbol, String}())
39-
@test_throws Exception createUser(dfgError, user)
40-
4125
@test getUserData(dfg) == Dict{Symbol, String}()
4226
@test getRobotData(dfg) == Dict{Symbol, String}()
4327
@test getSessionData(dfg) == Dict{Symbol, String}()
4428

4529
# User/robot/session data
4630
user.data = Dict{Symbol, String}(:a => "Hello", :b => "Goodbye")
47-
robot.data = Dict{Symbol, String}(:c => "Hello", :d => "Goodbye")
48-
session.data = Dict{Symbol, String}(:e => "Hello", :f => "Goodbye")
31+
robot.data = Dict{Symbol, String}(:c => "ABC", :d => "Goodbye")
32+
session.data = Dict{Symbol, String}(:e => "ADG", :f => "Goodbye")
4933

5034
setUserData!(dfg, user.data)
5135
setRobotData!(dfg, robot.data)
@@ -54,6 +38,9 @@ setSessionData!(dfg, session.data)
5438
@test getRobotData(dfg) == robot.data
5539
@test getSessionData(dfg) == session.data
5640

41+
# And nuke it so we can try the addVariable etc. functions to make sure they create the nodes.
42+
clearUser!!(dfg)
43+
createDfgSessionIfNotExist(dfg)
5744
# Add some nodes.
5845
v1 = addVariable!(dfg, :a, ContinuousScalar, tags = [:POSE])
5946
v2 = addVariable!(dfg, :b, ContinuousScalar, tags = [:POSE])
@@ -69,3 +56,9 @@ dfgLocal = buildSubgraph(LightDFG, dfg, union(ls(dfg), lsf(dfg)))
6956
# Confirm that with sentinels we still have the same graph (doesn't pull in the sentinels)
7057
@test symdiff(ls(dfgLocal), ls(dfg)) == []
7158
@test symdiff(lsf(dfgLocal), lsf(dfg)) == []
59+
60+
# Test errors
61+
dfgError = deepcopy(dfg)
62+
# User/robot/session ID's can't start with numbers and can't have spaces.
63+
dfgError.userId = "I want to fail"
64+
@test_throws Exception createDfgSessionIfNotExist(dfgError)

test/iifInterfaceTests.jl

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,9 @@ end
1010
@testset "Building a simple Graph" begin
1111
global dfg,v1,v2,f1
1212
# Use IIF to add the variables and factors
13-
v1 = addVariable!(dfg, :a, ContinuousScalar, tags = [:POSE], solvable=0)
14-
v2 = addVariable!(dfg, :b, ContinuousScalar, tags = [:LANDMARK], solvable=1)
13+
v1 = addVariable!(dfg, :a, Position{1}, tags = [:POSE], solvable=0)
14+
v2 = addVariable!(dfg, :b, Position{1}, tags = [:LANDMARK], solvable=1)
1515
f1 = addFactor!(dfg, [:a; :b], LinearRelative(Normal(50.0,2.0)), solvable=0)
16-
17-
@show dfg
18-
@show f1
19-
@show v1
2016
end
2117

2218
println()
@@ -25,15 +21,15 @@ println()
2521
#test before anything changes
2622
@testset "Producing Dot Files" begin
2723
global dfg
28-
@show todotstr = toDot(dfg)
24+
todotstr = toDot(dfg)
2925
#TODO consider using a regex, but for now test all orders
3026
todota = cmp(todotstr, "graph graphname {\n2 [\"label\"=\"a\",\"shape\"=\"ellipse\",\"fillcolor\"=\"red\",\"color\"=\"red\"]\n2 -- 3\n3 [\"label\"=\"abf1\",\"shape\"=\"box\",\"fillcolor\"=\"blue\",\"color\"=\"blue\"]\n1 [\"label\"=\"b\",\"shape\"=\"ellipse\",\"fillcolor\"=\"red\",\"color\"=\"red\"]\n1 -- 3\n}\n") |> abs
3127
todotb = cmp(todotstr, "graph graphname {\n2 [\"label\"=\"b\",\"shape\"=\"ellipse\",\"fillcolor\"=\"red\",\"color\"=\"red\"]\n2 -- 3\n3 [\"label\"=\"abf1\",\"shape\"=\"box\",\"fillcolor\"=\"blue\",\"color\"=\"blue\"]\n1 [\"label\"=\"a\",\"shape\"=\"ellipse\",\"fillcolor\"=\"red\",\"color\"=\"red\"]\n1 -- 3\n}\n") |> abs
3228
todotc = cmp(todotstr, "graph G {\na [color=red, shape=ellipse];\nb [color=red, shape=ellipse];\nabf1 [color=blue, shape=box];\na -- abf1\nb -- abf1\n}\n") |> abs
3329
todotd = cmp(todotstr, "graph G {\na [color=red, shape=ellipse];\nb [color=red, shape=ellipse];\nabf1 [color=blue, shape=box];\nb -- abf1\na -- abf1\n}\n") |> abs
3430
todote = cmp(todotstr, "graph G {\na [color=red, shape=ellipse];\nb [color=red, shape=ellipse];\nabf1 [color=blue, shape=box, fontsize=8, fixedsize=false, height=0.1, width=0.1];\na -- abf1\nb -- abf1\n}\n") |> abs
3531
todotf = cmp(todotstr, "graph G {\na [color=red, shape=ellipse];\nb [color=red, shape=ellipse];\nabf1 [color=blue, shape=box, fontsize=8, fixedsize=false, height=0.1, width=0.1];\nb -- abf1\na -- abf1\n}\n") |> abs
36-
@show todota, todotb, todotc, todotd, todote, todotf
32+
# @show todota, todotb, todotc, todotd, todote, todotf
3733
@test (todota < 1 || todotb < 1 || todotc < 1 || todotd < 1 || todote < 1 || todotf < 1)
3834
@test toDotFile(dfg, "something.dot") === nothing
3935
Base.rm("something.dot")
@@ -53,9 +49,9 @@ end
5349

5450
# Build a new in-memory IIF graph to transfer into the new graph.
5551
iiffg = initfg()
56-
v1 = deepcopy(addVariable!(iiffg, :a, ContinuousScalar))
57-
v2 = deepcopy(addVariable!(iiffg, :b, ContinuousScalar))
58-
v3 = deepcopy(addVariable!(iiffg, :c, ContinuousScalar))
52+
v1 = deepcopy(addVariable!(iiffg, :a, Position{1}))
53+
v2 = deepcopy(addVariable!(iiffg, :b, Position{1}))
54+
v3 = deepcopy(addVariable!(iiffg, :c, Position{1}))
5955
f1 = deepcopy(addFactor!(iiffg, [:a; :b], LinearRelative(Normal(50.0,2.0)) ))
6056
f2 = deepcopy(addFactor!(iiffg, [:b; :c], LinearRelative(Normal(10.0,1.0)) ))
6157

@@ -116,7 +112,7 @@ end
116112

117113
# Accessors
118114
@test getAddHistory(dfg) == [:a, :b] #, :abf1
119-
@test getDescription(dfg) != nothing
115+
@test getDescription(dfg) !== nothing
120116
#TODO Deprecate
121117
# @test_throws ErrorException getLabelDict(dfg)
122118
# Existence
@@ -151,15 +147,14 @@ end
151147
@test lsf(dfg, LinearRelative) == [:abf1]
152148
@test lsfWho(dfg, :LinearRelative) == [:abf1]
153149

154-
@test getVariableType(v1) isa ContinuousScalar
155-
@test getVariableType(dfg,:a) isa ContinuousScalar
150+
@test getVariableType(v1) isa Position{1}
151+
@test getVariableType(dfg,:a) isa Position{1}
156152

157153
#TODO what is lsTypes supposed to return?
158154
@test_broken lsTypes(dfg)
159155

160-
@test issetequal(ls(dfg, ContinuousScalar), [:a, :b])
161-
162-
@test issetequal(lsWho(dfg, :ContinuousScalar),[:a, :b])
156+
@test issetequal(ls(dfg, Position{1}), [:a, :b])
157+
@test issetequal(lsWho(dfg, :Position),[:a, :b])
163158

164159
varNearTs = findVariableNearTimestamp(dfg, now())
165160
@test_skip varNearTs[1][1] == [:b]
@@ -198,15 +193,15 @@ end
198193
@test getVariablePPEDict(v1) == v1.ppeDict # changed to .ppeDict -- delete by DFG v0.7
199194

200195

201-
@test typeof(getVariableType(v1)) == ContinuousScalar
202-
@test typeof(getVariableType(v2)) == ContinuousScalar
203-
@test typeof(getVariableType(v1)) == ContinuousScalar
196+
@test typeof(getVariableType(v1)) == Position{1}
197+
@test typeof(getVariableType(v2)) == Position{1}
198+
@test typeof(getVariableType(v1)) == Position{1}
204199

205200
@test getLabel(f1) == f1.label
206201
@test getTags(f1) == f1.tags
207202
@test getSolverData(f1) == f1.solverData
208203

209-
@test getSolverParams(dfg) != nothing
204+
@test getSolverParams(dfg) !== nothing
210205
@test setSolverParams!(dfg, getSolverParams(dfg)) == getSolverParams(dfg)
211206

212207
#solver data is initialized
@@ -329,10 +324,10 @@ end
329324
@test isConnected(dfg) == true
330325
# @test @test_deprecated isFullyConnected(dfg) == true
331326
# @test @test_deprecated hasOrphans(dfg) == false
332-
addVariable!(dfg, :orphan, ContinuousScalar, tags = [:POSE], solvable=0)
327+
addVariable!(dfg, :orphan, Position{1}, tags = [:POSE], solvable=0)
333328
@test isConnected(dfg) == false
334329
else
335-
addVariable!(dfg, :orphan, ContinuousScalar, tags = [:POSE], solvable=0)
330+
addVariable!(dfg, :orphan, Position{1}, tags = [:POSE], solvable=0)
336331
@warn "Neo4jDFG is currently failing with the connectivity test."
337332
end
338333
end
@@ -393,7 +388,7 @@ numNodes = 10
393388
# end
394389

395390
#change solvable and solveInProgress for x7,x8 for improved tests on x7x8f1
396-
verts = map(n -> addVariable!(dfg, Symbol("x$n"), ContinuousScalar, tags = [:POSE]), 1:numNodes)
391+
verts = map(n -> addVariable!(dfg, Symbol("x$n"), Position{1}, tags = [:POSE]), 1:numNodes)
397392
#TODO fix this to use accessors
398393
setSolvable!(verts[7], 1)
399394
setSolvable!(verts[8], 0)

test/testBlocks.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ function DFGStructureAndAccessors(::Type{T}, solparams::AbstractParams=NoSolverP
172172
@test getSessionId(fg) == sId
173173
@test getAddHistory(fg) === fg.addHistory
174174

175+
# Need to set this for Neo4jDFG
176+
@test setUserData!(fg, Dict(ud)) == Dict(ud)
177+
@test setRobotData!(fg, Dict(rd)) == Dict(rd)
178+
@test setSessionData!(fg, Dict(sd)) == Dict(sd)
175179
@test getUserData(fg) == Dict(ud)
176180
@test getRobotData(fg) == Dict(rd)
177181
@test getSessionData(fg) == Dict(sd)

0 commit comments

Comments
 (0)