|  |  | @ -113,7 +113,7 @@ func LinkRequirement(ctx *Context, dependency *Node, requirement NodeID) error { | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   dep_ext.Requirements[requirement] = ReqState{"linking", "unlocked"} |  |  |  |   dep_ext.Requirements[requirement] = ReqState{"linking", "unlocked"} | 
			
		
	
		
		
			
				
					
					|  |  |  |   return ctx.Send(dependency.ID, requirement, NewLinkSignal("req_link")) |  |  |  |   return ctx.Send(dependency.ID, requirement, NewLinkSignal("link_as_req")) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | func (ext *LockableExt) HandleLockSignal(ctx *Context, source NodeID, node *Node, signal StateSignal) { |  |  |  | func (ext *LockableExt) HandleLockSignal(ctx *Context, source NodeID, node *Node, signal StateSignal) { | 
			
		
	
	
		
		
			
				
					|  |  | @ -272,62 +272,69 @@ func (ext *LockableExt) HandleLockSignal(ctx *Context, source NodeID, node *Node | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | // TODO: don't allow changes to requirements or dependencies while being locked or locked
 |  |  |  | // TODO: don't allow changes to requirements or dependencies while being locked or locked
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | // TODO: add unlink
 | 
			
		
	
		
		
			
				
					
					|  |  |  | func (ext *LockableExt) HandleLinkSignal(ctx *Context, source NodeID, node *Node, signal StateSignal) { |  |  |  | func (ext *LockableExt) HandleLinkSignal(ctx *Context, source NodeID, node *Node, signal StateSignal) { | 
			
		
	
		
		
			
				
					
					|  |  |  |   ctx.Log.Logf("lockable", "LINK_SIGNAL: %s->%s %+v", source, node.ID, signal) |  |  |  |   ctx.Log.Logf("lockable", "LINK_SIGNAL: %s->%s %+v", source, node.ID, signal) | 
			
		
	
		
		
			
				
					
					|  |  |  |   state := signal.State |  |  |  |   state := signal.State | 
			
		
	
		
		
			
				
					
					|  |  |  |   switch state { |  |  |  |   switch state { | 
			
		
	
		
		
			
				
					
					|  |  |  |   // sent by a node to link this node as a requirement
 |  |  |  |   case "link_as_dep": | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |   case "req_link": |  |  |  |     state, exists := ext.Requirements[source] | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     _, exists := ext.Requirements[source] |  |  |  |     if exists == true && state.Link == "linked" { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     if exists == false { |  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("already_req")) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       dep_state, exists := ext.Dependencies[source] |  |  |  |     } else if state.Link == "linking" { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       if exists == false { |  |  |  |       state.Link = "linked" | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ext.Dependencies[source] = "linking" |  |  |  |       ext.Requirements[source] = state | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ctx.Send(node.ID, source, NewLinkSignal("dep_link")) |  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("linked_as_dep")) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       } else if dep_state == "linking" { |  |  |  |     } else if ext.PendingOwner != ext.Owner { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       if ext.Owner == nil { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         ctx.Send(node.ID, source, NewLinkSignal("locking")) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         ctx.Send(node.ID, source, NewLinkSignal("unlocking")) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       ext.Requirements[source] = ReqState{"linking", "unlocked"} | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("link_as_req")) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   case "link_as_req": | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     state, exists := ext.Dependencies[source] | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     if exists == true && state == "linked" { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("already_dep")) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } else if state == "linking" { | 
			
		
	
		
		
			
				
					
					|  |  |  |       ext.Dependencies[source] = "linked" |  |  |  |       ext.Dependencies[source] = "linked" | 
			
		
	
		
		
			
				
					
					|  |  |  |         ctx.Send(node.ID, source, NewLinkSignal("dep_linked")) |  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("linked_as_req")) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } else if ext.PendingOwner != ext.Owner { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       if ext.Owner == nil { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         ctx.Send(node.ID, source, NewLinkSignal("locking")) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         ctx.Send(node.ID, source, NewLinkSignal("unlocking")) | 
			
		
	
		
		
			
				
					
					|  |  |  |       } |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } else { |  |  |  |     } else { | 
			
		
	
		
		
			
				
					
					|  |  |  |       delete(ext.Requirements, source) |  |  |  |       ext.Dependencies[source] = "linking" | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("req_reset")) |  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("link_as_dep")) | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |   case "dep_link": |  |  |  |   case "linked_as_dep": | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     _, exists := ext.Dependencies[source] |  |  |  |     state, exists := ext.Dependencies[source] | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     if exists == false { |  |  |  |     if exists == false { | 
			
		
	
		
		
			
				
					
					|  |  |  |       req_state, exists := ext.Requirements[source] |  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("not_linking")) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       if exists == false { |  |  |  |     } else if state == "linked" { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ext.Requirements[source] = ReqState{"linking", "unlocked"} |  |  |  |     } else if state == "linking" { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ctx.Send(node.ID, source, NewLinkSignal("req_link")) |  |  |  |       ext.Dependencies[source] = "linked" | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       } else if req_state.Link == "linking" { |  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("linked_as_req")) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         req_state.Link = "linked" |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         ext.Requirements[source] = req_state |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         ctx.Send(node.ID, source, NewLinkSignal("req_linked")) |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } else { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       delete(ext.Dependencies, source) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("dep_reset")) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   case "dep_reset": |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     ctx.Log.Logf("lockable", "%s reset %s dependency state", node.ID, source) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   case "req_reset": |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     ctx.Log.Logf("lockable", "%s reset %s requirement state", node.ID, source) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |   case "dep_linked": |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     ctx.Log.Logf("lockable", "%s is a dependency of %s", node.ID, source) |  |  |  |     ctx.Log.Logf("lockable", "%s is a dependency of %s", node.ID, source) | 
			
		
	
		
		
			
				
					
					|  |  |  |     req_state, exists := ext.Requirements[source] |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     if exists == true && req_state.Link == "linking" { |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       req_state.Link = "linked" |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       ext.Requirements[source] = req_state |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("req_linked")) |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   case "req_linked": |  |  |  |   case "linked_as_req": | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     ctx.Log.Logf("lockable", "%s is a requirement of %s", node.ID, source) |  |  |  |     state, exists := ext.Requirements[source] | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     dep_state, exists := ext.Dependencies[source] |  |  |  |     if exists == false { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     if exists == true && dep_state == "linking" { |  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("not_linking")) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       ext.Dependencies[source] = "linked" |  |  |  |     } else if state.Link == "linked" { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("dep_linked")) |  |  |  |     } else if state.Link == "linking" { | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       state.Link = "linked" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       ext.Requirements[source] = state | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       ctx.Send(node.ID, source, NewLinkSignal("linked_as_dep")) | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     ctx.Log.Logf("lockable", "%s is a requirement of %s", node.ID, source) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   default: |  |  |  |   default: | 
			
		
	
		
		
			
				
					
					|  |  |  |     ctx.Log.Logf("lockable", "LINK_ERROR: unknown state %s", state) |  |  |  |     ctx.Log.Logf("lockable", "LINK_ERROR: unknown state %s", state) | 
			
		
	
	
		
		
			
				
					|  |  | 
 |