1
0
Fork 0
fgdata/Nasal/emesary.nas
Richard Harrison 4ed25ac361 Emesary mp support
Add support for transfer string
Change Type to NotificationType as Type can be confusing
2016-04-12 23:25:47 +02:00

297 lines
10 KiB
Text

#---------------------------------------------------------------------------
#
# Title : EMESARY inter-object communication
#
# File Type : Implementation File
#
# Description : Provides generic inter-object communication. For an object to receive a message it
# : must first register with an instance of a Transmitter, and provide a Receive method
#
# : To send a message use a Transmitter with an object. That's all there is to it.
#
# References : http://chateau-logic.com/content/emesary-nasal-implementation-flightgear
# : http://www.chateau-logic.com/content/class-based-inter-object-communication
# : http://chateau-logic.com/content/emesary-efficient-inter-object-communication-using-interfaces-and-inheritance
# : http://chateau-logic.com/content/c-wpf-application-plumbing-using-emesary
#
# Author : Richard Harrison (richard@zaretto.com)
#
# Creation Date : 29 January 2016
#
# Version : 4.8
#
# Copyright © 2016 Richard Harrison Released under GPL V2
#
#---------------------------------------------------------------------------*/
var __emesaryUniqueId = 14; # 0-15 are reserved, this way the global transmitter will be 15.
# Transmitters send notifications to all recipients that are registered.
var Transmitter =
{
ReceiptStatus_OK : 0, # Processing completed successfully
ReceiptStatus_Fail : 1, # Processing resulted in at least one failure
ReceiptStatus_Abort : 2, # Fatal error, stop processing any further recipieints of this message. Implicitly failed.
ReceiptStatus_Finished : 3, # Definitive completion - do not send message to any further recipieints
ReceiptStatus_NotProcessed : 4,# Return value when method doesn't process a message.
ReceiptStatus_Pending : 5, # Message sent with indeterminate return status as processing underway
ReceiptStatus_PendingFinished : 6,# Message definitively handled, status indeterminate. The message will not be sent any further
# create a new transmitter. shouldn't need many of these
new: func(_ident)
{
var new_class = { parents: [Transmitter]};
new_class.Recipients = [];
new_class.Ident = _ident;
__emesaryUniqueId += 1;
new_class.UniqueId = __emesaryUniqueId;
return new_class;
},
# Add a recipient to receive notifications from this transmitter
Register: func (recipient)
{
append(me.Recipients, recipient);
},
# Stops a recipient from receiving notifications from this transmitter.
DeRegister: func(todelete_recipient)
{
var out_idx = 0;
var element_deleted = 0;
for (var idx = 0; idx < size(me.Recipients); idx += 1)
{
if (me.Recipients[idx] != todelete_recipient)
{
me.Recipients[out_idx] = me.Recipients[idx];
out_idx = out_idx + 1;
}
else
element_deleted = 1;
}
if (element_deleted)
pop(me.Recipients);
},
RecipientCount: func
{
return size(me.Recipients);
},
PrintRecipients: func
{
print("Recpient list for ",me.Ident,"(",me.UniqueId,")");
for (var idx = 0; idx < size(me.Recipients); idx += 1)
print("Recpient ",idx," ",me.Recipients[idx].Ident," (",me.Recipients[idx].UniqueId,")");
},
# Notify all registered recipients. Stop when receipt status of abort or finished are received.
# The receipt status from this method will be
# - OK > message handled
# - Fail > message not handled. A status of Abort from a recipient will result in our status
# being fail as Abort means that the message was not and cannot be handled, and
# allows for usages such as access controls.
NotifyAll: func(message)
{
var return_status = Transmitter.ReceiptStatus_NotProcessed;
foreach (var recipient; me.Recipients)
{
if (recipient.RecipientActive)
{
var rstat = recipient.Receive(message);
if(rstat == Transmitter.ReceiptStatus_Fail)
{
return_status = Transmitter.ReceiptStatus_Fail;
}
elsif(rstat == Transmitter.ReceiptStatus_Pending)
{
return_status = Transmitter.ReceiptStatus_Pending;
}
elsif(rstat == Transmitter.ReceiptStatus_PendingFinished)
{
return rstat;
}
# elsif(rstat == Transmitter.ReceiptStatus_NotProcessed)
# {
# ;
# }
elsif(rstat == Transmitter.ReceiptStatus_OK)
{
if (return_status == Transmitter.ReceiptStatus_NotProcessed)
return_status = rstat;
}
elsif(rstat == Transmitter.ReceiptStatus_Abort)
{
return Transmitter.ReceiptStatus_Abort;
}
elsif(rstat == Transmitter.ReceiptStatus_Finished)
{
return Transmitter.ReceiptStatus_OK;
}
}
}
return return_status;
},
# Returns true if a return value from NotifyAll is to be considered a failure.
IsFailed: func(receiptStatus)
{
# Failed is either Fail or Abort.
# NotProcessed isn't a failure because it hasn't been processed.
if (receiptStatus == Transmitter.ReceiptStatus_Fail or receiptStatus == Transmitter.ReceiptStatus_Abort)
return 1;
return 0;
}
};
#
#
# Base class for Notifications. By convention a Notification has a type and a value.
# SubClasses can add extra properties or methods.
# Properties:
# Ident : Generic message identity. Can be an ident, or for simple messages a value that needs transmitting.
# NotificationType : Notification Type
# IsDistinct : non zero if this message supercedes previous messages of this type.
# Distinct messages are usually sent often and self contained
# (i.e. no relative state changes such as toggle value)
# Messages that indicate an event (such as after a pilot action)
# will usually be non-distinct. So an example would be gear/up down
# or ATC acknowledgements that all need to be transmitted
# The IsDistinct is important for any messages that are bridged over MP as
# only the most recently sent distinct message will be transmitted over MP
var Notification =
{
new: func(_type, _ident)
{
var new_class = { parents: [Notification]};
new_class.Ident = _ident;
new_class.NotificationType = _type;
new_class.IsDistinct = 1;
new_class.FromIncomingBridge = 0;
new_class.Callsign = nil;
return new_class;
},
};
# Inherit or implement class with the same signatures to receive messages.
var Recipient =
{
new: func(_ident)
{
var new_class = { parents: [Recipient]};
if (_ident == nil or _ident == "")
{
_ident = id(new_class);
print("ERROR: Ident required when creating a recipient, defaulting to ",_ident);
}
Recipient.construct(_ident, new_class);
},
construct: func(_ident, new_class)
{
new_class.Ident = _ident;
new_class.RecipientActive = 1;
__emesaryUniqueId += 1;
new_class.UniqueId = __emesaryUniqueId;
new_class.Receive = func(notification)
{
# warning if required function not
print("Emesary Error: Receive function not implemented in recipient ",me.Ident);
return Transmitter.ReceiptStatus_NotProcessed;
};
return new_class;
},
};
#
# Instantiate a Global Transmitter, this is a convenience and a known starting point. Generally most classes will
# use this transmitters, however other transmitters can be created and merely use the global transmitter to discover each other
var GlobalTransmitter = Transmitter.new("GlobalTransmitter");
var TransferCoord =
{
encode : func(v)
{
return mp_broadcast.Binary.encodeCoord(v);
},
decode : func(v)
{
return mp_broadcast.Binary.decodeCoord(v);
}
};
var TransferString =
{
#
# just to pack a valid range and keep the lower and very upper control codes for seperators
# that way we don't need to do anything special to encode the string.
getalphanumericchar : func(v)
{
if (find(v,"-./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz") > 0)
return v;
return nil;
},
encode : func(v)
{
var l = size(v);
if (l > 16)
l = 16;
var rv = mp_broadcast.Binary.encodeByte(l);
print ("Encode string ",v," l=",l);
for(var ii = 0; ii < l; ii = ii + 1)
{
printf("%d,%d (%s)\n",ii+1,1,rv);
ev = TransferString.getalphanumericchar(substr(v,ii,1));
if (ev != nil)
rv = rv ~ ev;
}
print("String encode l=",l," val=",rv);
return rv;
},
decode : func(v)
{
var l = mp_broadcast.Binary.decodeByte(v);
var rv = substr(v,1,l-1);
print("String decode l=",l," val=",rv);
return rv;
}
};
var TransferByte =
{
encode : func(v)
{
return mp_broadcast.Binary.encodeByte(v);
},
decode : func(v)
{
return mp_broadcast.Binary.decodeByte(v);
}
};
var TransferInt =
{
encode : func(v)
{
return mp_broadcast.Binary.encodeInt(v);
},
decode : func(v)
{
return mp_broadcast.Binary.decodeInt(v);
}
};
var TransferDouble =
{
encode : func(v)
{
return mp_broadcast.Binary.encodeDouble(v);
},
decode : func(v)
{
return mp_broadcast.Binary.decodeDouble(v);
}
};