1
0
Fork 0
fgdata/Nasal/emesary_mp_bridge.nas
2016-04-09 21:39:57 +02:00

319 lines
14 KiB
Text

#
# bridge message rules;
# - if distinct must be absolute and self contained as a later message will supercede any earlier ones in the outgoing queue.
# - use the message type and ident to identify distinct messages
# The outgoing 'port' is a multiplay/generic/string index.
# - ! is used as a seperator between the elements that are used to send the notification (typeid, sequence, notification)
# - ; is used to seperate serialzied elemnts of the notification
#
# Outgoing messages are sent in a scheduled manner, usually once per second, and each message has a lifetime (to allow for propogation to
# all clients over UDP). Clients will ignore messages that they have already received (based on the sequence id).
#
# The incoming bridge will usually be created part of the aircraft model file; it is important to understand that each AI/MP model will have an incoming bridge
# as each element in /ai/models needs its own bridge to keep up with the incoming sequence id. This scheme may not work properly as it relies on the model being
# loaded which may only happen when visible so it may be necessary to track AI models in a seperate instantiatable incoming bridge manager.
#
# The outgoing bridge would usually be created within the aircraft loading Nasal.
var OutgoingMPBridge =
{
new: func(_ident, _notifications_to_bridge=nil, _mpidx=19, _root="", _transmitter=nil)
{
if (_transmitter == nil)
_transmitter = emesary.GlobalTransmitter;
print("OutgoingMPBridge created for "~_ident);
var new_class = emesary.Recipient.new("OutgoingMPBridge "~_ident);
new_class.MessageIndex = 100;
# foreach (var notification; _notifications_to_bridge)
# new_class.NotificationsToBridge = notification.new(;
if(_notifications_to_bridge == nil)
new_class.NotificationsToBridge = [];
else
new_class.NotificationsToBridge = _notifications_to_bridge;
new_class.MPout = "";
new_class.MPidx = _mpidx;
new_class.MessageLifeTime = 10; # seconds
new_class.OutgoingList = [];
new_class.Transmitter = _transmitter;
new_class.TransmitRequired=0;
new_class.Transmitter.Register(new_class);
new_class.MpVariable = _root~"sim/multiplay/generic/string["~new_class.MPidx~"]";
new_class.TransmitterActive = 0;
new_class.TransmitTimer =
maketimer(6, func
{
if(new_class.TransmitterActive)
new_class.Transmit();
new_class.TransmitTimer.restart(1);
});
new_class.TransmitTimer.restart(1);
new_class.Delete = func
{
if (me.Transmitter != nil) {
me.Transmitter.DeRegister(me);
me.Transmitter = nil;
}
};
new_class.AddMessage = func(m)
{
append(me.NotificationsToBridge, m);
};
#-------------------------------------------
# Receive override:
new_class.Receive = func(notification)
{
if (notification.FromIncomingBridge)
return emesary.Transmitter.ReceiptStatus_NotProcessed;
#print("Receive ",notification.Type," (",notification.Ident);
for (var idx = 0; idx < size(me.NotificationsToBridge); idx += 1)
{
if(me.NotificationsToBridge[idx].Type == notification.Type)
{
me.MessageIndex += 1;
notification.MessageExpiryTime = systime()+me.MessageLifeTime;
notification.BridgeMessageId = me.MessageIndex;
notification.BridgeMessageTypeId = idx;
#
# The message key is a composite of the type and ident to allow for multiple senders
# of the same message type.
notification.BridgeMessageTypeKey = notification.Type~"."~notification.Ident;
#print("Received ",notification.BridgeMessageTypeKey," expire=",notification.MessageExpiryTime);
me.AddToOutgoing(notification);
return emesary.Transmitter.ReceiptStatus_Pending;
}
}
return emesary.Transmitter.ReceiptStatus_NotProcessed;
};
new_class.AddToOutgoing = func(notification)
{
if (notification.IsDistinct)
{
for (var idx = 0; idx < size(me.OutgoingList); idx += 1)
{
if(me.OutgoingList[idx].BridgeMessageTypeKey == notification.BridgeMessageTypeKey)
{
#print("Update ",me.OutgoingList[idx].BridgeMessageTypeKey);
me.OutgoingList[idx]= notification;
me.TransmitterActive = size(me.OutgoingList);
return;
}
}
}
#print("Added ",notification.BridgeMessageTypeKey);
append(me.OutgoingList, notification);
me.TransmitterActive = size(me.OutgoingList);
};
new_class.Transmit = func
{
var outgoing = "";
var cur_time=systime();
var out_idx = 0;
for (var idx = 0; idx < size(me.OutgoingList); idx += 1)
{
var sect = "";
var notification = me.OutgoingList[idx];
if (notification.MessageExpiryTime > cur_time)
{
var encval="";
var first_time = 1;
var eidx = 0;
foreach(var p ; notification.bridgeProperties())
{
if (encval != "")
encval = encval ~ ";";
encval = encval ~ p.getValue();
#print("Encode ",eidx,"=",encval);
eidx += 1;
}
sect = sprintf("%d!%d!%s",notification.BridgeMessageId, notification.BridgeMessageTypeId, encval);
outgoing = outgoing~sect;
me.OutgoingList[out_idx] = me.OutgoingList[idx];
# print("xmit ",idx," out=",out_idx);
out_idx += 1;
}
# else
# printf("expired ",idx,out_idx);
}
me.TransmitterActive = size(me.OutgoingList);
var del_count = size(me.OutgoingList)-out_idx;
setprop(me.MpVariable,outgoing);
# print("Set ",me.MpVariable," to ",outgoing);
# print("outgoingList : ",out_idx, " ", size(me.OutgoingList), " del count=",del_count);
for(var del_i=0; del_i < del_count; del_i += 1)
pop(me.OutgoingList);
};
return new_class;
},
};
#
#
# one of these for each model instantiated in the model XML - it will
# route messages to
var IncomingMPBridge =
{
new: func(_ident, _notifications_to_bridge=nil, _mpidx=19, _transmitter=nil)
{
if (_transmitter == nil)
_transmitter = emesary.GlobalTransmitter;
print("IncominggMPBridge created for "~_ident);
var new_class = emesary.Transmitter.new("IncominggMPBridge "~_ident);
new_class.IncomingMessageIndex = 100;
if(_notifications_to_bridge == nil)
new_class.NotificationsToBridge = [];
else
new_class.NotificationsToBridge = _notifications_to_bridge;
new_class.MPout = "";
new_class.MPidx = _mpidx;
new_class.MessageLifeTime = 10; # seconds
new_class.OutgoingList = [];
new_class.Transmitter = _transmitter;
new_class.MpVariable = "";
new_class.Connect = func(_root)
{
me.MpVariable = _root~"sim/multiplay/generic/string["~new_class.MPidx~"]";
me.Callsign = getprop(_root~"callsign");
print("bridge ",me.MpVariable, " callsign ",me.Callsign);
setlistener(me.MpVariable, func(v)
{
me.ProcessIncoming(v.getValue());
});
};
new_class.AddMessage = func(m)
{
append(me.NotificationsToBridge, m);
};
new_class.Remove = func
{
print("Emesary IncomingMPBridge Remove() ",me.Ident);
me.Transmitter.DeRegister(me);
};
#-------------------------------------------
# Receive override:
new_class.ProcessIncoming = func(encoded_val)
{
if (encoded_val == "")
return;
# print("ProcessIncoming ", encoded_val);
var encoded_notifications = split(";", encoded_val);
for (var idx = 0; idx < size(encoded_notifications); idx += 1)
{
# get the message parts
var encoded_notification = split("!", encoded_val);
if (size(encoded_notification) < 3)
print("Error: emesary.IncomingBridge.ProcessIncoming bad msg ",encoded_val);
else
{
var msg_idx = encoded_notification[0];
var msg_type_id = encoded_notification[1];
if (msg_type_id >= size(me.NotificationsToBridge))
{
print("Error: emesary.IncomingBridge.ProcessIncoming invalid type_id ",msg_type_id);
}
else
{
var msg = me.NotificationsToBridge[msg_type_id];
var msg_notify = encoded_notification[2];
print("received idx=",msg_idx," ",msg_type_id,":",msg.Type);
if(msg_idx <= me.IncomingMessageIndex)
print(" **Already processed");
else
{
# raise notification
var bridged_notification = msg; #emesary.Notification.new(msg.Type,"BridgedMessage");
# populate fields
var bridgedProperties = msg.bridgeProperties();
var encvals=split(";", msg_notify);
for (var bpi = 0; bpi < size(encvals); bpi += 1) {
if (bpi < size(bridgedProperties)) {
var bp = bridgedProperties[bpi];
print("encval ",bpi,"=",encvals[bpi]);
if (encvals[bpi] != ";" and encvals[bpi] != "") {
var bp = bridgedProperties[bpi];
bp.setValue(encvals[bpi]);
}
#else
#print("EMPTY encoded ",bpi," empty");
} else
print("Error: emesary.IncomingBridge.ProcessIncoming: supplementary encoded value at",bpi);
}
if (bridged_notification.Ident == "none")
bridged_notification.Ident = "mp-bridge";
bridged_notification.FromIncomingBridge = 1;
bridged_notification.Callsign = me.Callsign;
me.Transmitter.NotifyAll(bridged_notification);
me.IncomingMessageIndex = msg_idx;
}
}
}
}
}
foreach(var n; new_class.NotificationsToBridge)
{
print("IncomingBridge: ",n.Type);
}
return new_class;
},
startMPBridge : func(notification_list)
{
var incomingBridgeList = {};
setlistener("/ai/models/model-added", func(v)
{
#Model added /ai[0]/models[0]/multiplayer[0]
var path = v.getValue();
print("Model added ",path);
if (find("/multiplayer",path) > 0)
{
var callsign = getprop(path~"/callsign");
print("Creating Emesary MPBridge for ",callsign);
if (callsign == "" or callsign == nil)
callsign = path;
var incomingBridge = emesary_mp_bridge.IncomingMPBridge.new(callsign~"mp", notification_list, 19);
incomingBridge.Connect(path~"/");
incomingBridgeList[path] = incomingBridge;
}
});
setlistener("/ai/models/model-removed", func(v){
print("Model removed ",v.getValue());
var path = v.getValue();
var bridge = incomingBridgeList[path];
if (bridge != nil)
{
bridge.Remove();
incomingBridgeList[path]=nil;
}
});
},
};
#to setup bridges;
#io.load_nasal(getprop("/sim/fg-root") ~ "/Nasal/emesary_mp_bridge.nas");
#var outgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("F-15mp");
#outgoingBridge.AddMessage(an_spn_46.ANSPN46ActiveNotification.new("template"));
#incomingBridge = emesary_mp_bridge.IncomingMPBridge.new("F-15mp");
#incomingBridge.AddMessage(an_spn_46.ANSPN46ActiveNotification.new("template"));
#var outgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("F-15mp");
#outgoingBridge.AddMessage(an_spn_46.ANSPN46ActiveNotification.new("template"));
# var m = emesary.notifications.TacticalNotification("rr",2);
# emesary.GlobalTransmitter.NotifyAll(m);
#var outgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("F-15mp");
#outgoingBridge.AddMessage(notifications.TacticalNotification.new(nil));
# var m = emesary.notifications.TacticalNotification("rr",2);
# emesary.GlobalTransmitter.NotifyAll(m);