1
0
Fork 0

Added Emesary and AN/SPN-46 ACLS

Emesary is a simple and efficient class based interobject communcation system to allow decoupled disparate parts of a system to function together without knowing about each. It allows decoupling and removal of dependencies by using notifications to cause actions or to query values.

Emesary is all about decoupling and removing dependecies, and improving the structure of code. Using Emesary you can more easily define the what rather than the how. By using what is essential an event driven system it is easy to add or remove modules, and also for extra modules to be inserted that the rest of the aircraft knows nothing about (e.g. FGCamera or the Walker).

see: http://chateau-logic.com/content/emesary-nasal-implementation-flightgear

The AN/SPN-46 is an ACLS implementation using Emesary. ACLS is the Navy's version of ILS.
This commit is contained in:
Richard Harrison 2016-04-02 00:13:35 +02:00
parent 4bc3619fdb
commit 54165c213f
6 changed files with 683 additions and 70 deletions

View file

@ -0,0 +1,330 @@
#---------------------------------------------------------------------------
#
# Title : AN/SPN-46 Precision Approach Landing System (PALS)
#
# File Type : Implementation File
#
# Description : Representative emulation of the functions of the AN/SPN-46 using emesary
# : Where an AN/SPN-46 is required it is sufficient to instantiate a ANSPN46_System connected to the instantiator model.
# : Register with an instance of a Transmitter, and provide a Receive method, periodically send out Active messages
# : expecting ActiveResponse messages which may result in a Communication.
# : This is implemented as a set of messages that models the operation of the PALS
#
# References : http://trace.tennessee.edu/cgi/viewcontent.cgi?article=3297&context=utk_gradthes
# : http://www.navair.navy.mil/index.cfm?fuseaction=home.displayPlatform&key=E8D18768-14B6-4CF5-BAB5-12B009070CFC
# : http://www.afceaboston.com/documents/events/cnsatm2010/Briefs/4%20-%20Friday/03-Navy_Landing_Systems_Roadmap-(CDR%20Easler).pdf
#
# Author : Richard Harrison (richard@zaretto.com)
#
# Creation Date : 29 January 2016
#
# Version : 4.8
#
# Copyright © 2016 Richard Harrison Released under GPL V2
#
#---------------------------------------------------------------------------*/
#Message Reference:
#---------------------------------------------------------------------------*/
# Notification 1 ANSPN46ActiveNotification from carrier to aircraft
# Transmitted via GlobalTransmitter at 1hz
# - carrier position
# - beam start
# - beam angle
# - channel / frequency information
# - beam range / power
#
# Notification 2 - from aircraft to carrier
# - aircraft position
# - radar return size
# - aircraft altitude, heading, velocity
# - respose indicating tuned or not
#
# Notification 3 - from carrier to aircraft
# - lateral deviation
# - vertical deviation
# - LSO information / messages
# - system status
# - lateral
#
#Operation:
#---------------------
# The ANSPN64 system will send periodically send out a ANSPN46ActiveNotification
# the rest of the logic within this system related to aircraft will be handled when
# the ANSPN46ActiveResponseNotification - which itself in turn will send out a ANSPN46CommunicationNotification
#----------------------
# NOTE: to avoid garbage collection all of the notifications that are sent out are created during construction
# and simply modified prior to sending. This works because emesary is synchronous and therefore the state
# and lifetime are known.
#
# Notification(1) from carrier to any aircraft within range.
#
var ANSPN46ActiveNotification =
{
# Create a new active notification notification. Aircraft will respond to this.
# param(_anspn46_system): instance of ANSPN46_System which will send the notification
new: func(_anspn46_system)
{
var new_class = emesary.Notification.new("ANSPN46ActiveNotification", _anspn46_system.Ident);
new_class.ANSPN46_system = _anspn46_system;
#
# Set notification properties from the ANSPN46_System.
new_class.set_from = func(_anspn)
{
me.Position = _anspn.GetCarrierPosition();
me.BeamPosition = _anspn.GetTDZPosition();
me.BeamAngle = 35;
me.Channel = _anspn.GetChannel();
me.BeamRange = 35; ##nm
me.BeamPower = 999; ## mw ???
};
new_class.set_from(_anspn46_system);
return new_class;
},
};
# Notification(2) - from aircraft to carrier sent in response to Notification(1) above
#
var ANSPN46ActiveResponseNotification =
{
new: func(_ident)
{
var new_class = emesary.Notification.new("ANSPN46ActiveResponseNotification", _ident);
new_class.Respond = func(_msg)
{
new_class.Position = geo.aircraft_position();
new_class.Heading = getprop("orientation/heading-deg");
new_class.RadarReturnStrength = 1; # normalised value based on RCS beam power etc.
new_class.Tuned = 0; # 0 or 1
new_class.ufps = getprop("velocities/uBody-fps");
return me;
}
return new_class;
},
};
# Notification 3 - from carrier to aircraft as a result of active response notification
# - only sent if the aircraft is set to the same channel that we are transmitting on
#
var ANSPN46CommunicationNotification =
{
new: func(_ident, _anspn46_system)
{
var new_class = emesary.Notification.new("ANSPN46CommunicationNotification", _ident);
new_class.Model = _anspn46_system.Model;
new_class.set_from = func(_ident, _msg, _anspn46_system)
{
var carrier_ara_63_position = _anspn46_system.GetCarrierPosition();
var carrier_heading = _anspn46_system.GetCarrierHeading();
var carrier_ara_63_heading = 0;
# relative offset of the course to the tdz
# according to my measurements the Nimitz class is 8.1362114 degrees (measured 178 vs carrier 200 allowing for local magvar -13.8637886)
# i.e. this value is from tuning rather than calculation
if (carrier_heading != nil)
carrier_ara_63_heading = carrier_heading.getValue() - 8.1362114;
var range = _msg.Position.distance_to(carrier_ara_63_position);
var bearing_to = _msg.Position.course_to(carrier_ara_63_position);
var deviation = bearing_to - carrier_ara_63_heading;
deviation = deviation *0.1;
me.ReturnPosition = _msg.Position;
me.ReturnBearing = getprop("orientation/heading-deg");
# the AN/SPN 46 has a 20nm range with a 3 degree beam; ref: F14-AAD-1 17.3.2
if (range < 37000 and abs(deviation) < 3)
{
var FD_TAN3DEG = math.tan(3.0 / 57.29577950560105);
var deck_height=20;
var gs_height = ((range*FD_TAN3DEG)) + deck_height;
var gs_deviation = (gs_height - _msg.Position.alt()) / 42.0;
if (gs_deviation > 1)
gs_deviation = 1;
else if (gs_deviation < -1)
gs_deviation = -1;
# if not in range message will not be transmitted.
me.InRange = 1;
# calculate the deviation from the ideal approach
me.VerticalAdjustmentCommanded = gs_deviation;
me.HorizontalAdjustmentCommanded = deviation;
me.LateralDeviation = deviation;
me.VerticalDeviation = gs_deviation;
me.Distance = range;
me.SignalQualityNorm = 1;
#
# work out the rough ETA for the 10 seconds light, and use this
# to decide whether or not to waveoff
var eta = range / (_msg.ufps / 3.281);
if(eta <= 10 and range < 800 and range > 150)
{
me.TenSeconds = 1;
if(math.abs(deviation) > 0.2 or math.abs(gs_deviation) > 0.2)
{
me.WaveOff = 1;
}
else
{
me.WaveOff = 0;
}
}
else
{
me.TenSeconds = 0;
me.WaveOff = 0;
}
me.LSOMessage = "";
me.SystemStatus = "OK"; # Wave off, error, etcc.
}
else
{
#
# No response will be sent when not in range; so ensure values are all cleared.
me.InRange = 0;
me.VerticalAdjustmentCommanded = 0;
me.HorizontalAdjustmentCommanded = 0;
me.SignalQualityNorm =0;
me.Distance = -10000000000;
me.TenSeconds = 0;
me.WaveOff = 0;
me.InRange = 0;
me.VerticalDeviation = 0;
me.LateralDeviation = 0;
me.LSOMessage = "";
me.SystemStatus = "";
}
};
return new_class;
},
};
#
# The main AN/SPN46 system implemented using emesary.
# Periodically the Update method should be called, which will
# send out a notification via the global transmitter so that aircraft within range
# can respond. This is similar to a radar transmit and return.
# There should be an instance of this class created in the nasal section of the model xml
# Once an aircraft is within range it will receive guidance that can be displayed.
# It is the responsibility of the AN/SPN system to decided whether a craft is within range.
# It is the responsibility of the aircraft receiver to indicate whether it is tuned in or not
# if it the aircraft is not tuned into the right channel the receiver (e.g. ARA-63) will not receive anything; however
# the AN/SPN system (being a radar) will still have guidance information that could be relayed over the
# comms channel or displayed on a radar display on the carrier.
#
var ANSPN46_System =
{
new: func(_ident,_model)
{
print("AN/SNP46 created for "~_ident);
var new_class = emesary.Recipient.new("ANSPN46_System "~_ident);
new_class.ara_63_position = geo.Coord.new();
new_class.Model = _model;
new_class.communicationNotification = ANSPN46CommunicationNotification.new(new_class.Ident, new_class);
new_class.Channel = 2;
new_class.UpdateRate = 10;
#-------------------------------------------
# Receive override:
# Iinterested in receiving ANSPN46ActiveResponseNotification. When we get
# one of these we can respond with a CommunicationNotification
new_class.Receive = func(notification)
{
if (notification.Type == "ANSPN46ActiveResponseNotification")
{
if (notification.Tuned)
{
me.communicationNotification.set_from(me.Ident, notification, me);
if(me.communicationNotification.InRange)
{
me.UpdateRate = 0.2;
emesary.GlobalTransmitter.NotifyAll(me.communicationNotification);
}
}
return emesary.Transmitter.ReceiptStatus_OK;
}
return emesary.Transmitter.ReceiptStatus_NotProcessed;
}
#
# Interface methods
#-----------------------------
# Required interface to get the current carrier position
new_class.GetCarrierPosition = func()
{
var x = me.Model.getNode("position/global-x").getValue() + 88.7713542;
var y = me.Model.getNode("position/global-y").getValue() + 18.74631309;
var z = me.Model.getNode("position/global-z").getValue() + 115.6574875;
me.ara_63_position.set_xyz(x, y, z);
return me.ara_63_position;
};
#
# Interface to get the carrier heading
new_class.GetCarrierHeading = func()
{
return me.Model.getNode("orientation/true-heading-deg");
};
#
# offset of the TDZ (wires) from the carrier centre
new_class.GetTDZPosition = func
{
return me.ara_63_position;
};
#
# radar beam angle
new_class.GetUpdateRate = func
{
return me.UpdateRate;
};
new_class.BeamAngle = func
{
return 30;
};
#
# currently transmitting channel number.
new_class.GetChannel = func
{
return me.Channel;
};
new_class.SetChannel = func(v)
{
me.Channel = v;
};
#
# main entry point. The object itself will manage the update rate - but it is
# up to the caller to use this rate
new_class.Update = func
{
# fill in properties of message
me.msg.set_from(me);
#
# manage the update rate; increase each frame until we get to 10 seconds
# this will be reset if we receive something back from the aircraft.
if (me.UpdateRate < 10)
me.UpdateRate = me.UpdateRate+1;
# print("AN/SPN 46 : update from",me.Ident," rate=",me.UpdateRate);
return emesary.GlobalTransmitter.NotifyAll(me.msg);
};
#
# create the message that will be used to notify of an active carrier. This needs to be done after the methods
# have been created as it references them. Implemented like this to reduce garbage collection
new_class.msg = ANSPN46ActiveNotification.new(new_class);
emesary.GlobalTransmitter.Register(new_class);
return new_class;
},
}

View file

@ -13,6 +13,32 @@
<PropertyList>
<path>clem-superstructure.ac</path>
<nasal>
<load>
<![CDATA[
# add AN/SPN-46 see http://chateau-logic.com/content/emesary-nasal-implementation-flightgear
var self = cmdarg();
fn_an_spn_46 = getprop("/sim/fg-root") ~ "/Aircraft/Generic/an_spn_46.nas";
io.load_nasal(fn_an_spn_46, "an_spn_46");
var anspn = an_spn_46.ANSPN46_System.new("Clemenceau", self);
anspn.SetChannel(2);
var an_spn_46_timer = maketimer(6, func {
anspn.Update();
an_spn_46_timer.restart(anspn.GetUpdateRate());
});
an_spn_46_timer.restart(6);
]]>
</load>
<unload>
<![CDATA[
an_spn_46_timer.stop();
]]>
</unload>
</nasal>
<texture-path>Textures</texture-path>
<offsets>
<z-m>0</z-m>

View file

@ -8,7 +8,32 @@ apart from most of the very excellent textures, remain. -->
<status>early-production</status>
<path>nimitz.ac</path>
<!-- crew to be added later
<nasal>
<load>
<![CDATA[
var self = cmdarg();
print("Model load Nimitz ", self.getPath());
fn_an_spn_46 = getprop("/sim/fg-root") ~ "/Aircraft/Generic/an_spn_46.nas";
io.load_nasal(fn_an_spn_46, "an_spn_46");
var anspn = an_spn_46.ANSPN46_System.new("Eisenhower", self);
anspn.SetChannel(2);
var an_spn_46_timer = maketimer(6, func {
anspn.Update();
an_spn_46_timer.restart(anspn.GetUpdateRate());
});
an_spn_46_timer.restart(6);
]]>
</load>
<unload>
<![CDATA[
an_spn_46_timer.stop();
]]>
</unload>
</nasal>
<!-- crew to be added later
<model>
<path>Models/Geometry/Nimitz/Crew/crew.xml</path>

View file

@ -8,7 +8,32 @@ apart from most of the very excellent textures, remain. -->
<status>early-production</status>
<path>nimitz.ac</path>
<!-- crew to be added later
<nasal>
<load>
<![CDATA[
var self = cmdarg();
print("Model load Nimitz ", self.getPath());
fn_an_spn_46 = getprop("/sim/fg-root") ~ "/Aircraft/Generic/an_spn_46.nas";
io.load_nasal(fn_an_spn_46, "an_spn_46");
var anspn = an_spn_46.ANSPN46_System.new("Nimitz", self);
anspn.SetChannel(2);
var an_spn_46_timer = maketimer(6, func {
anspn.Update();
an_spn_46_timer.restart(anspn.GetUpdateRate());
});
an_spn_46_timer.restart(6);
]]>
</load>
<unload>
<![CDATA[
an_spn_46_timer.stop();
]]>
</unload>
</nasal>
<!-- crew to be added later
<model>
<path>Models/Geometry/Nimitz/Crew/crew.xml</path>
</model>

View file

@ -10,91 +10,104 @@ apart from most of the very excellent textures, remain. -->
<nasal>
<load>
print("LOAD Vinson ", cmdarg().getPath());
<![CDATA[
print("LOAD Vinson ", cmdarg().getPath());
var fg_root = getprop("/sim/fg-root");
var self = cmdarg();
var control_node = self.getNode("controls/turn-to-base-course", 1);
var pos_node = self.getNode("sim/antenna-pos-norm", 1);
pos_node.setDoubleValue(0);
var turn_old = 1;
var fg_root = getprop("/sim/fg-root");
var self = cmdarg();
var control_node = self.getNode("controls/turn-to-base-course", 1);
var pos_node = self.getNode("sim/antenna-pos-norm", 1);
pos_node.setDoubleValue(0);
var turn_old = 1;
########
# properties used to handle rendering, lighting and to control the Pack-Park
# Due to a change in MPcarrier system ?
# add AN/SPN-46 see http://chateau-logic.com/content/emesary-nasal-implementation-flightgear
fn_an_spn_46 = getprop("/sim/fg-root") ~ "/Aircraft/Generic/an_spn_46.nas";
io.load_nasal(fn_an_spn_46, "an_spn_46");
var anspn = an_spn_46.ANSPN46_System.new("Eisenhower", self);
anspn.SetChannel(2);
# not using the main update as need to keep the scheduling that an_spn_46 requests.
var an_spn_46_timer = maketimer(6, func {
anspn.Update();
an_spn_46_timer.restart(anspn.GetUpdateRate());
});
an_spn_46_timer.restart(6);
########
# properties used to handle rendering, lighting and to control the Pack-Park
# Due to a change in MPcarrier system ?
var rembrandt_node = self.getNode("sim/rendering/rembrandt/enabled", 1);
var sunAngleRad_node = self.getNode("sim/time/sun-angle-rad", 1);
var deckPark_node = self.getNode("sim/current-view/deck-park",1);
var rembrandt_node = self.getNode("sim/rendering/rembrandt/enabled", 1);
var sunAngleRad_node = self.getNode("sim/time/sun-angle-rad", 1);
var deckPark_node = self.getNode("sim/current-view/deck-park",1);
########
# properties used to calculate rel wind for the bow-wave shader
########
# properties used to calculate rel wind for the bow-wave shader
var speed_Node = self.getNode("velocities/speed-kts", 1);
var hdg_Node = self.getNode("orientation/true-heading-deg", 1);
var wind_speed_Node = self.getNode("environment/rel-wind-speed-kts", 1);
wind_speed_Node.setDoubleValue(0);
var speed_Node = self.getNode("velocities/speed-kts", 1);
var hdg_Node = self.getNode("orientation/true-heading-deg", 1);
var wind_speed_Node = self.getNode("environment/rel-wind-speed-kts", 1);
wind_speed_Node.setDoubleValue(0);
########
# properties to control the E2C
########
# properties to control the E2C
var gear0_Node = self.getNode("gear/gear[0]/position-norm", 1);
gear0_Node.setDoubleValue(1);
var gear0_Node = self.getNode("gear/gear[0]/position-norm", 1);
gear0_Node.setDoubleValue(1);
var gear1_Node = self.getNode("gear/gear[1]/position-norm", 1);
gear1_Node.setDoubleValue(1);
var gear1_Node = self.getNode("gear/gear[1]/position-norm", 1);
gear1_Node.setDoubleValue(1);
var gear2_Node = self.getNode("gear/gear[2]/position-norm", 1);
gear2_Node.setDoubleValue(1);
var gear2_Node = self.getNode("gear/gear[2]/position-norm", 1);
gear2_Node.setDoubleValue(1);
var compression0_Node = self.getNode("gear/gear[0]/compression-norm", 1);
compression0_Node.setDoubleValue(0);
var compression0_Node = self.getNode("gear/gear[0]/compression-norm", 1);
compression0_Node.setDoubleValue(0);
var compression1_Node = self.getNode("gear/gear[1]/compression-norm", 1);
compression1_Node.setDoubleValue(0.9);
var compression1_Node = self.getNode("gear/gear[1]/compression-norm", 1);
compression1_Node.setDoubleValue(0.9);
var compression2_Node = self.getNode("gear/gear[2]/compression-norm", 1);
compression2_Node.setDoubleValue(0.9);
var compression2_Node = self.getNode("gear/gear[2]/compression-norm", 1);
compression2_Node.setDoubleValue(0.9);
var wingfold_Node = self.getNode("surface-positions/wing-fold-pos-norm", 1);
wingfold_Node.setDoubleValue(1.0);
var wingfold_Node = self.getNode("surface-positions/wing-fold-pos-norm", 1);
wingfold_Node.setDoubleValue(1.0);
var rpm0_Node = self.getNode("engines/engine[0]/rpm", 1);
rpm0_Node.setDoubleValue(0.15);
var rpm0_Node = self.getNode("engines/engine[0]/rpm", 1);
rpm0_Node.setDoubleValue(0.15);
var rpm1_Node = self.getNode("engines/engine[1]/rpm", 1);
rpm1_Node.setDoubleValue(0.17);
var rpm1_Node = self.getNode("engines/engine[1]/rpm", 1);
rpm1_Node.setDoubleValue(0.17);
########
# the main loop
########
# the main loop
var update = func {
var turn = control_node.getValue();
var value = wind_speed_Node.getValue();
setprop("/environment/Vinson/rel-wind-speed-kts", value);
value = speed_Node.getValue();
setprop("/environment/Vinson/spd-kt", value);
value = hdg_Node.getValue();
setprop("/environment/Vinson/hdg-deg", value);
var update = func {
var turn = control_node.getValue();
var value = wind_speed_Node.getValue();
setprop("/environment/Vinson/rel-wind-speed-kts", value);
value = speed_Node.getValue();
setprop("/environment/Vinson/spd-kt", value);
value = hdg_Node.getValue();
setprop("/environment/Vinson/hdg-deg", value);
value = getprop("/sim/rendering/rembrandt/enabled");
rembrandt_node.setBoolValue(value);
value = getprop("/sim/current-view/deck-park");
deckPark_node.setBoolValue(value);
value = getprop("/sim/time/sun-angle-rad");
sunAngleRad_node.setValue(value);
value = getprop("/sim/rendering/rembrandt/enabled");
rembrandt_node.setBoolValue(value);
value = getprop("/sim/current-view/deck-park");
deckPark_node.setBoolValue(value);
value = getprop("/sim/time/sun-angle-rad");
sunAngleRad_node.setValue(value);
if (turn_old != turn){
turn_old = turn;
move_whips();
}
if (turn_old != turn){
turn_old = turn;
move_whips();
}
settimer(update,0);
}
settimer(update,0);
}
var move_whips = func {
var whip_pos = pos_node.getValue();
if (whip_pos &lt;=1 and whip_pos &gt;=0 and turn_old == 0){
var move_whips = func {
var whip_pos = pos_node.getValue();
if (whip_pos &lt;=1 and whip_pos &gt;=0 and turn_old == 0){
whip_pos += 0.001;
if (whip_pos &gt; 1)
@ -119,13 +132,16 @@ apart from most of the very excellent textures, remain. -->
########
#start the main loop
update();
</load>
]]>
</load>
</nasal>
<unload>
#print("UNLOAD Vinson ", cmdarg().getPath());
</unload>
<![CDATA[
#print("UNLOAD Vinson ", cmdarg().getPath());
an_spn_46_timer.stop();
]]>
</unload>
<!-- crew to be added later
<model>

191
Nasal/emesary.nas Normal file
View file

@ -0,0 +1,191 @@
#---------------------------------------------------------------------------
#
# 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
#
#---------------------------------------------------------------------------*/
# 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;
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 (var idx = 0; idx < size(me.Recipients); idx += 1)
print("Recpient ",idx," ",me.Recipients[idx].Ident);
},
# 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.Active)
{
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.
var Notification =
{
new: func(_type, _value)
{
var new_class = { parents: [Notification]};
new_class.Value = _value;
new_class.Type = _type;
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.Active = 1;
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");