Emesary fixes
- Transfer encoding rewritten to handle negative numbers properly and fix scaling. - Change the "no receive method" to be just a warning as it is unwise rather than being wrong. Emesary MP bridge fixes from Nikolai OutgoingBridge: - When transmitting queue, make sure transmitted messages cannot remain in the queue when not completely emptied, and thus be sent again, and eventually fill up the queue. - Also allow smaller messages to sent even though there was not room for a larger message (the removal of the 'break' command). - To prevent coding the same message again with the 'break' removed, store an already coded string that was rejected due to too little room inside the message so when there is room for it, it was coded only once. - Stop recalculating MessageExpiryTime when there is no room for a notification. If it has asked to expire after expire time, let it expire. IncomingBridge: - Make sure listeners on emesary[x] properties are removed when a mp aircraft is invalid, otherwise a hidden bridge will sit in background and process same notifications a new bridge is already processing. - Change order of setting IncomingMessageIndex to ease debugging in reciever. - Since a bridge class can handle multiple emesary properties on same aircraft, use a vector per aircraft to store the bridge instances in, to make sure Remove can be called on all instances listening to same aircraft.
This commit is contained in:
parent
37729a8661
commit
3666ba1511
2 changed files with 263 additions and 260 deletions
|
@ -20,7 +20,7 @@
|
|||
#
|
||||
# Version : 4.8
|
||||
#
|
||||
# Copyright © 2016 Richard Harrison Released under GPL V2
|
||||
# Copyright © 2016 Richard Harrison Released under GPL V2
|
||||
#
|
||||
#---------------------------------------------------------------------------
|
||||
# Classes in this file:
|
||||
|
@ -95,11 +95,12 @@ var Transmitter =
|
|||
{
|
||||
logprint(LOG_INFO, "Transmitter.Register: argument is not a Recipient object");
|
||||
}
|
||||
#not having a Receive function is an error
|
||||
# Warn if recipient doesn't have a Receive function - this is not an error because
|
||||
#a receive function could be added after the recipient has been registered - so it is
|
||||
# deprecated to do this.
|
||||
if (!isfunc(recipient["Receive"]))
|
||||
{
|
||||
logprint(DEV_ALERT, "Transmitter.Register: Error, argument has no Receive method!");
|
||||
return 0;
|
||||
}
|
||||
foreach (var r; me.Recipients)
|
||||
{
|
||||
|
@ -309,18 +310,30 @@ var Notification =
|
|||
logprint(DEV_ALERT, "Notification.new: _type must be a scalar!");
|
||||
return nil;
|
||||
}
|
||||
if (!isscalar(_ident)) {
|
||||
if (!isscalar(_ident) and _ident != nil) {
|
||||
logprint(DEV_ALERT, "Notification.new: _ident is not scalar but ", typeof(_ident));
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (_typeid == 0) {
|
||||
NotificationAutoTypeId += 1;
|
||||
# typeID of 0 means that the notification does not have an assigned type ID
|
||||
# <0 means an automatic ID is required
|
||||
# >= 16 is a reserved ID
|
||||
# normally the typeID should be unique across all of FlightGear.
|
||||
# use of automatic ID's is really only for notifications that will never be bridged,
|
||||
# or more accurate when bridged the type isn't going to be known fully.
|
||||
|
||||
if (_typeid < 0) {
|
||||
if (_ident != nil){
|
||||
logprint(DEV_ALERT, "_typeid can only be omitted when registering class");
|
||||
return nil;
|
||||
}
|
||||
|
||||
# IDs >= 16 are reserved; see http://wiki.flightgear.org/Emesary_Notifications
|
||||
if (NotificationAutoTypeId == 16) {
|
||||
if (NotificationAutoTypeId >= 16) {
|
||||
logprint(LOG_ALERT, "Notification: AutoTypeID limit exceeded: "~NotificationAutoTypeId);
|
||||
return nil;
|
||||
}
|
||||
NotificationAutoTypeId += 1;
|
||||
_typeid = NotificationAutoTypeId;
|
||||
}
|
||||
|
||||
|
@ -411,8 +424,8 @@ var BinaryAsciiTransfer =
|
|||
{
|
||||
#excluded chars 32 (<space>), 33 (!), 35 (#), 36($), 126 (~), 127 (<del>)
|
||||
alphabet :
|
||||
chr(1) ~chr(2) ~chr(3) ~chr(4) ~chr(5) ~chr(6) ~chr(7) ~chr(8) ~chr(9)
|
||||
~chr(10)~chr(11)~chr(12)~chr(13)~chr(14)~chr(15)~chr(16)~chr(17)~chr(18)~chr(19)
|
||||
chr(1)~chr(2)~chr(3)~chr(4)~chr(5)~chr(6)~chr(7)~chr(8)
|
||||
~chr(9)~chr(10)~chr(11)~chr(12)~chr(13)~chr(14)~chr(15)~chr(16)~chr(17)~chr(18)~chr(19)
|
||||
~chr(20)~chr(21)~chr(22)~chr(23)~chr(24)~chr(25)~chr(26)~chr(27)~chr(28)~chr(29)
|
||||
~chr(30)~chr(31)~chr(34)
|
||||
~"%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}"
|
||||
|
@ -439,13 +452,12 @@ var BinaryAsciiTransfer =
|
|||
empty_encoding: chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1)~chr(1),
|
||||
encodeNumeric : func(_num,length,factor)
|
||||
{
|
||||
#print(BinaryAsciiTransfer.po2[1]);
|
||||
var irange = int(BinaryAsciiTransfer.po2[length] / factor);
|
||||
var scale = int(irange / factor);
|
||||
#print("EC ",irange, " sc=",scale);
|
||||
var num = int(_num / factor);
|
||||
if (num < -scale) num = -scale;
|
||||
else if (num > scale) num = scale;
|
||||
|
||||
var irange = int(BinaryAsciiTransfer.po2[length]);
|
||||
|
||||
if (num < -irange) num = -irange;
|
||||
else if (num > irange) num = irange;
|
||||
|
||||
num = int(num + irange);
|
||||
|
||||
|
@ -467,7 +479,7 @@ var BinaryAsciiTransfer =
|
|||
retval : {value:0, pos:0},
|
||||
decodeNumeric : func(str, length, factor, pos)
|
||||
{
|
||||
var irange = int(BinaryAsciiTransfer.po2[length]/factor);
|
||||
var irange = int(BinaryAsciiTransfer.po2[length]);
|
||||
var power = length-1;
|
||||
BinaryAsciiTransfer.retval.value = 0;
|
||||
BinaryAsciiTransfer.retval.pos = pos;
|
||||
|
@ -573,11 +585,11 @@ var TransferFixedDouble =
|
|||
{
|
||||
encode : func(v, length, factor)
|
||||
{
|
||||
return BinaryAsciiTransfer.encodeNumeric(int(v), length, factor);
|
||||
return BinaryAsciiTransfer.encodeNumeric(v, length, factor);
|
||||
},
|
||||
decode : func(v, length, factor, pos)
|
||||
{
|
||||
return BinaryAsciiTransfer.decodeNumeric(v, length, pos, factor);
|
||||
return BinaryAsciiTransfer.decodeNumeric(v, length, factor, pos);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -611,24 +623,27 @@ var TransferByte =
|
|||
|
||||
var TransferCoord =
|
||||
{
|
||||
# 28 bits = 268435456 (268 435 456)
|
||||
# to transfer lat lon (360 degree range) 268435456/360=745654
|
||||
# we could use different factors for lat lon due to the differing range, however
|
||||
# this will be fine.
|
||||
# LatLon scaling;
|
||||
# 1 degree = 110574 meters;
|
||||
# requires 4 bytes for 1 meter resolution.
|
||||
# permits 0.1 meter resolution.
|
||||
LatLonLength: 4,
|
||||
LatLonFactor: 0.000001,
|
||||
AltLength: 3,
|
||||
|
||||
encode : func(v)
|
||||
{
|
||||
return BinaryAsciiTransfer.encodeNumeric((v.lat()+90)*745654,5, 1.0)
|
||||
~ BinaryAsciiTransfer.encodeNumeric((v.lon()+180)*745654,5, 1.0)
|
||||
~ TransferNumeric.encode(v.alt(), 3, 1.0);
|
||||
return BinaryAsciiTransfer.encodeNumeric(v.lat(), TransferCoord.LatLonLength, TransferCoord.LatLonFactor)
|
||||
~ BinaryAsciiTransfer.encodeNumeric(v.lon(), TransferCoord.LatLonLength, TransferCoord.LatLonFactor)
|
||||
~ emesary.TransferInt.encode(v.alt(), TransferCoord.AltLength);
|
||||
},
|
||||
decode : func(v,pos)
|
||||
{
|
||||
var dv = BinaryAsciiTransfer.decodeNumeric(v,5, 1.0 ,pos);
|
||||
var lat = (dv.value / 745654)-90;
|
||||
dv = BinaryAsciiTransfer.decodeNumeric(v,5, 1.0 ,dv.pos);
|
||||
var lon = (dv.value / 745654)-180;
|
||||
dv = TransferNumeric.decode(v, 3, 1.0 ,dv.pos);
|
||||
var dv = BinaryAsciiTransfer.decodeNumeric(v, TransferCoord.LatLonLength, TransferCoord.LatLonFactor, pos);
|
||||
var lat = (dv.value);
|
||||
dv = BinaryAsciiTransfer.decodeNumeric(v, TransferCoord.LatLonLength, TransferCoord.LatLonFactor, dv.pos);
|
||||
var lon = (dv.value);
|
||||
dv = emesary.TransferInt.decode(v, TransferCoord.AltLength, dv.pos);
|
||||
var alt =dv.value;
|
||||
|
||||
dv.value = geo.Coord.new().set_latlon(lat, lon).set_alt(alt);
|
||||
|
|
|
@ -109,7 +109,6 @@ var OutgoingMPBridge =
|
|||
new_class.OutgoingList = [];
|
||||
new_class.Transmitter = _transmitter;
|
||||
new_class.TransmitRequired=0;
|
||||
new_class.Transmitter.Register(new_class);
|
||||
new_class.MpVariable = _root~"sim/multiplay/"~_propertybase~"["~new_class.MPidx~"]";
|
||||
new_class.TransmitterActive = 0;
|
||||
new_class.TransmitFrequencySeconds = 1;
|
||||
|
@ -162,13 +161,13 @@ var OutgoingMPBridge =
|
|||
}
|
||||
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.trace)
|
||||
print("Compare [",idx,"] qId=",me.OutgoingList[idx].GetBridgeMessageNotificationTypeKey() ," noti --> ",notification.GetBridgeMessageNotificationTypeKey());
|
||||
|
||||
if (me.OutgoingList[idx].GetBridgeMessageNotificationTypeKey() == notification.GetBridgeMessageNotificationTypeKey()) {
|
||||
if (me.trace)
|
||||
print(" --> Update ",me.OutgoingList[idx].GetBridgeMessageNotificationTypeKey() ," noti --> ",notification.GetBridgeMessageNotificationTypeKey());
|
||||
|
@ -177,8 +176,7 @@ var OutgoingMPBridge =
|
|||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
} else
|
||||
if (me.trace)
|
||||
print("Not distinct, adding always");
|
||||
|
||||
|
@ -191,13 +189,17 @@ var OutgoingMPBridge =
|
|||
{
|
||||
var outgoing = "";
|
||||
var cur_time=systime();
|
||||
var first_time = 1;
|
||||
me.OutgoingListNew = [];
|
||||
for (var idx = 0; idx < size(me.OutgoingList); idx += 1) {
|
||||
var sect = "";
|
||||
var notification = me.OutgoingList[idx];
|
||||
|
||||
if (!notification.Expired and notification.MessageExpiryTime > cur_time) {
|
||||
if (notification["sect"] == nil) {
|
||||
# This is first time attempting to transmit this notification
|
||||
var encval="";
|
||||
var first_time = 1;
|
||||
|
||||
var eidx = 0;
|
||||
notification.Expired = 0;
|
||||
|
||||
|
@ -211,53 +213,35 @@ var OutgoingMPBridge =
|
|||
OutgoingMPBridge.SeperatorChar, emesary.BinaryAsciiTransfer.encodeInt(notification.BridgeMessageId,4),
|
||||
OutgoingMPBridge.SeperatorChar, emesary.BinaryAsciiTransfer.encodeInt(notification.BridgeMessageNotificationTypeId,1),
|
||||
OutgoingMPBridge.SeperatorChar, encval, OutgoingMPBridge.MessageEndChar);
|
||||
|
||||
} else {
|
||||
# This notification has already been coded, but was previously not sent due to too little space.
|
||||
sect = notification.sect;
|
||||
}
|
||||
if (size(outgoing) + size(sect) < me.MPStringMaxLen) {
|
||||
outgoing = outgoing~sect;
|
||||
} else {
|
||||
if (first_time) {
|
||||
print("Emesary: ERROR [",me.Ident,"] out of space for ",notification.NotificationType, " transmitted count=",idx, " queue size ",size(me.OutgoingList));
|
||||
notification.MessageExpiryTime = systime()+me.MessageLifeTime;
|
||||
break;
|
||||
first_time = 0;
|
||||
}
|
||||
#notification.MessageExpiryTime = systime()+me.MessageLifeTime;
|
||||
#break;
|
||||
notification.sect = sect;
|
||||
append(me.OutgoingListNew, notification);
|
||||
}
|
||||
} else {
|
||||
notification.Expired = 1;
|
||||
}
|
||||
}
|
||||
me.OutgoingList = me.OutgoingListNew;
|
||||
me.TransmitterActive = size(me.OutgoingList);
|
||||
setprop(me.MpVariable,outgoing);
|
||||
# print("Set ",me.MpVariable," size(",size(outgoing));
|
||||
#loopback test:
|
||||
# incomingBridge.ProcessIncoming(outgoing);
|
||||
|
||||
#
|
||||
# Now remove any expired messages from the outgoing queue.
|
||||
# The only real way of doing this is via the pop() function
|
||||
# so we have to delete the expired items from the list by rebuilding
|
||||
# the list from the start with non-expired items, and then
|
||||
# all of the items past the end (of the rebuilt list) can be popped
|
||||
# (pop removes the last element from a vector)
|
||||
var outSize = size(me.OutgoingList)-1;
|
||||
var out_idx = 0;
|
||||
for (var idx = 0; idx <= outSize; idx += 1) {
|
||||
#print("Q1 [",idx,"] ",me.OutgoingList[idx].MessageExpiryTime-cur_time," Expired=",me.OutgoingList[idx].Expired);
|
||||
if(!me.OutgoingList[idx].Expired) {
|
||||
#print("move ",idx, " => ",out_idx);
|
||||
var mmove = me.OutgoingList[idx];
|
||||
me.OutgoingList[out_idx] = me.OutgoingList[idx];
|
||||
out_idx += 1;
|
||||
}
|
||||
}
|
||||
var to_del = (outSize+1) - out_idx;
|
||||
#print("--> out idx",out_idx, " to delete ",to_del);
|
||||
while(to_del > 0) {
|
||||
#print("--> pop ");
|
||||
pop(me.OutgoingList);
|
||||
to_del = to_del - 1;
|
||||
}
|
||||
};
|
||||
new_class.Transmitter.Register(new_class);
|
||||
new_class.TransmitTimer.restart(new_class.TransmitFrequencySeconds);
|
||||
return new_class;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -304,8 +288,7 @@ var IncomingMPBridge =
|
|||
me.MpVariable = _root~"sim/multiplay/"~new_class.MPpropertyBase~"["~new_class.MPidx~"]";
|
||||
me.CallsignPath = _root~"callsign";
|
||||
me.PropertyRoot = _root;
|
||||
|
||||
setlistener(me.MpVariable, func(v)
|
||||
me.mp_listener = setlistener(me.MpVariable, func(v)
|
||||
{
|
||||
#print("incoming ",getprop(me.CallsignPath)," -->",me.PropertyRoot," v=",v.getValue());
|
||||
me.ProcessIncoming(v.getValue());
|
||||
|
@ -327,8 +310,11 @@ var IncomingMPBridge =
|
|||
|
||||
new_class.Remove = func
|
||||
{
|
||||
print("Emesary IncomingMPBridge Remove() ",me.Ident);
|
||||
print("Emesary IncomingMPBridge Remove() ",me.Ident," Property: ",me.MpVariable);
|
||||
me.Transmitter.DeRegister(me);
|
||||
if (me["mp_listener"] != nil)
|
||||
removelistener(me.mp_listener);
|
||||
me.mp_listener = nil;
|
||||
};
|
||||
|
||||
#-------------------------------------------
|
||||
|
@ -373,22 +359,17 @@ var IncomingMPBridge =
|
|||
if (IncomingMPBridge.trace > 2)
|
||||
print("Process ",msg_body," len=",msglen, " BPsize = ",size(bridgedProperties));
|
||||
var pos = 0;
|
||||
|
||||
for (var bpi = 0; bpi < size(bridgedProperties); bpi += 1) {
|
||||
if (pos < msglen) {
|
||||
|
||||
if (IncomingMPBridge.trace > 2)
|
||||
print("dec: pos ",pos);
|
||||
|
||||
var bp = bridgedProperties[bpi];
|
||||
dv = bp.setValue(msg_body, me, pos);
|
||||
|
||||
if (IncomingMPBridge.trace > 2)
|
||||
print(" --> next pos ", dv.pos);
|
||||
|
||||
if (dv.pos == pos or dv.pos > msglen)
|
||||
break;
|
||||
|
||||
pos = dv.pos;
|
||||
} else {
|
||||
print("Error: emesary.IncomingBridge.ProcessIncoming: [",bridged_notification.NotificationType,"] supplementary encoded value at position ",bpi);
|
||||
|
@ -401,8 +382,9 @@ var IncomingMPBridge =
|
|||
|
||||
if (bridged_notification.Ident == "none")
|
||||
bridged_notification.Ident = "mp-bridge";
|
||||
me.Transmitter.NotifyAll(bridged_notification);
|
||||
|
||||
bridged_notification.IncomingMessageIndex = msg_idx;
|
||||
me.Transmitter.NotifyAll(bridged_notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -417,7 +399,11 @@ var IncomingMPBridge =
|
|||
var incomingBridge = emesary_mp_bridge.IncomingMPBridge.new(path, notification_list, mpidx, transmitter, _propertybase);
|
||||
|
||||
incomingBridge.Connect(path~"/");
|
||||
me.incomingBridgeList[path] = incomingBridge;
|
||||
if (me.incomingBridgeList[path] == nil) {
|
||||
me.incomingBridgeList[path] = [incomingBridge];
|
||||
} else {
|
||||
append(me.incomingBridgeList[path],incomingBridge);
|
||||
}
|
||||
return incomingBridge;
|
||||
},
|
||||
|
||||
|
@ -453,10 +439,12 @@ var IncomingMPBridge =
|
|||
#
|
||||
setlistener("/ai/models/model-removed", func(v){
|
||||
var path = v.getValue();
|
||||
var bridge = me.incomingBridgeList[path];
|
||||
if (bridge != nil) {
|
||||
# print("Bridge removed for ",v.getValue());
|
||||
var bridges = me.incomingBridgeList[path];
|
||||
if (bridges != nil) {
|
||||
foreach(bridge;bridges) {
|
||||
bridge.Remove();
|
||||
# print("Bridge removed for ",v.getValue());
|
||||
}
|
||||
me.incomingBridgeList[path] = nil;
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue