|
|
@ -5,11 +5,13 @@ import (
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// A Listener extension provides a channel that can receive signals on a different thread
|
|
|
|
type ListenerExt struct {
|
|
|
|
type ListenerExt struct {
|
|
|
|
Buffer int
|
|
|
|
Buffer int
|
|
|
|
Chan chan Signal
|
|
|
|
Chan chan Signal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create a new listener extension with a given buffer size
|
|
|
|
func NewListenerExt(buffer int) *ListenerExt {
|
|
|
|
func NewListenerExt(buffer int) *ListenerExt {
|
|
|
|
return &ListenerExt{
|
|
|
|
return &ListenerExt{
|
|
|
|
Buffer: buffer,
|
|
|
|
Buffer: buffer,
|
|
|
@ -17,6 +19,7 @@ func NewListenerExt(buffer int) *ListenerExt {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Simple load function, unmarshal the buffer int from json
|
|
|
|
func LoadListenerExt(ctx *Context, data []byte) (Extension, error) {
|
|
|
|
func LoadListenerExt(ctx *Context, data []byte) (Extension, error) {
|
|
|
|
var j int
|
|
|
|
var j int
|
|
|
|
err := json.Unmarshal(data, &j)
|
|
|
|
err := json.Unmarshal(data, &j)
|
|
|
@ -31,6 +34,7 @@ func (listener *ListenerExt) Type() ExtType {
|
|
|
|
return ListenerExtType
|
|
|
|
return ListenerExtType
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Send the signal to the channel, logging an overflow if it occurs
|
|
|
|
func (ext *ListenerExt) Process(ctx *Context, princ_id NodeID, node *Node, signal Signal) {
|
|
|
|
func (ext *ListenerExt) Process(ctx *Context, princ_id NodeID, node *Node, signal Signal) {
|
|
|
|
ctx.Log.Logf("signal", "LISTENER_PROCESS: %s - %+v", node.ID, signal)
|
|
|
|
ctx.Log.Logf("signal", "LISTENER_PROCESS: %s - %+v", node.ID, signal)
|
|
|
|
select {
|
|
|
|
select {
|
|
|
@ -41,16 +45,51 @@ func (ext *ListenerExt) Process(ctx *Context, princ_id NodeID, node *Node, signa
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ReqState holds the multiple states of a requirement
|
|
|
|
|
|
|
|
type ReqState struct {
|
|
|
|
|
|
|
|
Link string `json:"link"`
|
|
|
|
|
|
|
|
Lock string `json:"lock"`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// A LockableExt allows a node to be linked to other nodes(via LinkSignal) and locked/unlocked(via LockSignal)
|
|
|
|
|
|
|
|
type LockableExt struct {
|
|
|
|
|
|
|
|
Owner *NodeID
|
|
|
|
|
|
|
|
PendingOwner *NodeID
|
|
|
|
|
|
|
|
Requirements map[NodeID]ReqState
|
|
|
|
|
|
|
|
Dependencies map[NodeID]string
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type LockableExtJSON struct {
|
|
|
|
|
|
|
|
Owner *NodeID `json:"owner"`
|
|
|
|
|
|
|
|
PendingOwner *NodeID `json:"pending_owner"`
|
|
|
|
|
|
|
|
Requirements map[string]ReqState `json:"requirements"`
|
|
|
|
|
|
|
|
Dependencies map[string]string `json:"dependencies"`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Simple json load function: TODO: make these a generic function as before
|
|
|
|
func LoadLockableExt(ctx *Context, data []byte) (Extension, error) {
|
|
|
|
func LoadLockableExt(ctx *Context, data []byte) (Extension, error) {
|
|
|
|
var ext LockableExt
|
|
|
|
var j LockableExtJSON
|
|
|
|
err := json.Unmarshal(data, &ext)
|
|
|
|
err := json.Unmarshal(data, &j)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements, err := LoadIDMap(j.Requirements)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ctx.Log.Logf("db", "DB_LOADING_LOCKABLE_EXT_JSON: %+v", ext)
|
|
|
|
dependencies, err := LoadIDMap(j.Dependencies)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return &ext, nil
|
|
|
|
return &LockableExt{
|
|
|
|
|
|
|
|
Owner: j.Owner,
|
|
|
|
|
|
|
|
PendingOwner: j.PendingOwner,
|
|
|
|
|
|
|
|
Requirements: requirements,
|
|
|
|
|
|
|
|
Dependencies: dependencies,
|
|
|
|
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (ext *ListenerExt) Serialize() ([]byte, error) {
|
|
|
|
func (ext *ListenerExt) Serialize() ([]byte, error) {
|
|
|
@ -62,7 +101,12 @@ func (ext *LockableExt) Type() ExtType {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (ext *LockableExt) Serialize() ([]byte, error) {
|
|
|
|
func (ext *LockableExt) Serialize() ([]byte, error) {
|
|
|
|
return json.MarshalIndent(ext, "", " ")
|
|
|
|
return json.MarshalIndent(&LockableExtJSON{
|
|
|
|
|
|
|
|
Owner: ext.Owner,
|
|
|
|
|
|
|
|
PendingOwner: ext.PendingOwner,
|
|
|
|
|
|
|
|
Requirements: IDMap(ext.Requirements),
|
|
|
|
|
|
|
|
Dependencies: IDMap(ext.Dependencies),
|
|
|
|
|
|
|
|
}, "", " ")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func NewLockableExt() *LockableExt {
|
|
|
|
func NewLockableExt() *LockableExt {
|
|
|
@ -74,26 +118,17 @@ func NewLockableExt() *LockableExt {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type ReqState struct {
|
|
|
|
// Send the signal to unlock a node from itself
|
|
|
|
Link string `json:"link"`
|
|
|
|
|
|
|
|
Lock string `json:"lock"`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type LockableExt struct {
|
|
|
|
|
|
|
|
Owner *NodeID `json:"owner"`
|
|
|
|
|
|
|
|
PendingOwner *NodeID `json:"pending_owner"`
|
|
|
|
|
|
|
|
Requirements map[NodeID]ReqState `json:"requirements"`
|
|
|
|
|
|
|
|
Dependencies map[NodeID]string `json:"dependencies"`
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func UnlockLockable(ctx *Context, node *Node) error {
|
|
|
|
func UnlockLockable(ctx *Context, node *Node) error {
|
|
|
|
return ctx.Send(node.ID, node.ID, NewLockSignal("unlock"))
|
|
|
|
return ctx.Send(node.ID, node.ID, NewLockSignal("unlock"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Send the signal to lock a node from itself
|
|
|
|
func LockLockable(ctx *Context, node *Node) error {
|
|
|
|
func LockLockable(ctx *Context, node *Node) error {
|
|
|
|
return ctx.Send(node.ID, node.ID, NewLockSignal("lock"))
|
|
|
|
return ctx.Send(node.ID, node.ID, NewLockSignal("lock"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Setup a node to send the initial requirement link signal, then send the signal
|
|
|
|
func LinkRequirement(ctx *Context, dependency *Node, requirement NodeID) error {
|
|
|
|
func LinkRequirement(ctx *Context, dependency *Node, requirement NodeID) error {
|
|
|
|
dep_ext, err := GetExt[*LockableExt](dependency)
|
|
|
|
dep_ext, err := GetExt[*LockableExt](dependency)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
@ -114,6 +149,7 @@ func LinkRequirement(ctx *Context, dependency *Node, requirement NodeID) error {
|
|
|
|
return ctx.Send(dependency.ID, requirement, NewLinkSignal("link_as_req"))
|
|
|
|
return ctx.Send(dependency.ID, requirement, NewLinkSignal("link_as_req"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Handle a LockSignal and update the extensions owner/requirement states
|
|
|
|
func (ext *LockableExt) HandleLockSignal(ctx *Context, source NodeID, node *Node, signal StateSignal) {
|
|
|
|
func (ext *LockableExt) HandleLockSignal(ctx *Context, source NodeID, node *Node, signal StateSignal) {
|
|
|
|
ctx.Log.Logf("lockable", "LOCK_SIGNAL: %s->%s %+v", source, node.ID, signal)
|
|
|
|
ctx.Log.Logf("lockable", "LOCK_SIGNAL: %s->%s %+v", source, node.ID, signal)
|
|
|
|
state := signal.State
|
|
|
|
state := signal.State
|
|
|
@ -269,8 +305,8 @@ func (ext *LockableExt) HandleLockSignal(ctx *Context, source NodeID, node *Node
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: don't allow changes to requirements or dependencies while being locked or locked
|
|
|
|
// Handle LinkSignal, updating the extensions requirements and dependencies as necessary
|
|
|
|
// TODO: add unlink
|
|
|
|
// 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
|
|
|
@ -339,6 +375,8 @@ func (ext *LockableExt) HandleLinkSignal(ctx *Context, source NodeID, node *Node
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// LockableExts process Up/Down signals by forwarding them to owner, dependency, and requirement nodes
|
|
|
|
|
|
|
|
// LockSignal and LinkSignal Direct signals are processed to update the requirement/dependency/lock state
|
|
|
|
func (ext *LockableExt) Process(ctx *Context, source NodeID, node *Node, signal Signal) {
|
|
|
|
func (ext *LockableExt) Process(ctx *Context, source NodeID, node *Node, signal Signal) {
|
|
|
|
ctx.Log.Logf("signal", "LOCKABLE_PROCESS: %s", node.ID)
|
|
|
|
ctx.Log.Logf("signal", "LOCKABLE_PROCESS: %s", node.ID)
|
|
|
|
|
|
|
|
|
|
|
@ -389,88 +427,3 @@ func (ext *LockableExt) Process(ctx *Context, source NodeID, node *Node, signal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func SaveNode(node *Node) string {
|
|
|
|
|
|
|
|
str := ""
|
|
|
|
|
|
|
|
if node != nil {
|
|
|
|
|
|
|
|
str = node.ID.String()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return str
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func RestoreNode(ctx *Context, id_str string) (*Node, error) {
|
|
|
|
|
|
|
|
if id_str == "" {
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
id, err := ParseID(id_str)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return LoadNode(ctx, id)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func SaveNodeMap(nodes NodeMap) map[string]string {
|
|
|
|
|
|
|
|
m := map[string]string{}
|
|
|
|
|
|
|
|
for id, node := range(nodes) {
|
|
|
|
|
|
|
|
m[id.String()] = SaveNode(node)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return m
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func RestoreNodeMap(ctx *Context, ids map[string]string) (NodeMap, error) {
|
|
|
|
|
|
|
|
nodes := NodeMap{}
|
|
|
|
|
|
|
|
for id_str_1, id_str_2 := range(ids) {
|
|
|
|
|
|
|
|
id_1, err := ParseID(id_str_1)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
node_1, err := LoadNode(ctx, id_1)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var node_2 *Node = nil
|
|
|
|
|
|
|
|
if id_str_2 != "" {
|
|
|
|
|
|
|
|
id_2, err := ParseID(id_str_2)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
node_2, err = LoadNode(ctx, id_2)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nodes[node_1.ID] = node_2
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nodes, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func SaveNodeList(nodes NodeMap) []string {
|
|
|
|
|
|
|
|
ids := make([]string, len(nodes))
|
|
|
|
|
|
|
|
i := 0
|
|
|
|
|
|
|
|
for id, _ := range(nodes) {
|
|
|
|
|
|
|
|
ids[i] = id.String()
|
|
|
|
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ids
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func RestoreNodeList(ctx *Context, ids []string) (NodeMap, error) {
|
|
|
|
|
|
|
|
nodes := NodeMap{}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, id_str := range(ids) {
|
|
|
|
|
|
|
|
node, err := RestoreNode(ctx, id_str)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
nodes[node.ID] = node
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return nodes, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|