230 lines
9.7 KiB
Text
230 lines
9.7 KiB
Text
# failures.nas a manager for failing systems based on MTBF/MCBF
|
|
|
|
# Time between MTBF checks
|
|
var dt = 10;
|
|
|
|
# Root property for failure information
|
|
var failure_root = "/sim/failure-manager";
|
|
|
|
# Enumerations
|
|
var type = { MTBF : 1, MCBF: 2 };
|
|
var fail = { SERVICEABLE : 1, JAM : 2, ENGINE: 3};
|
|
|
|
# This hash contains a mapping from property entry to a failure object
|
|
# containing the following members:
|
|
# type: MTBF|MCBF Mean Time Between Failures/Mean Cycle Between Failures
|
|
# desc: <description> Description of property for screen output
|
|
# failure: SERVICEABLE Property has a "serviceable" child that can be set to false
|
|
# failure: JAM Property is failed by marking as Read-Only
|
|
# failure: ENGINE Special case for engines, where a variety of properties are set.
|
|
# failure: <prop> Property is failed by setting another property to false
|
|
var breakHash = {
|
|
"/instrumentation/adf" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "ADF" },
|
|
"/instrumentation/dme" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "DME" },
|
|
"/instrumentation/airspeed-indicator" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "ASI" },
|
|
"/instrumentation/altimeter" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Altimeter" },
|
|
"/instrumentation/attitude-indicator" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Attitude Indicator" },
|
|
"/instrumentation/heading-indicator" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Heading Indicator" },
|
|
"/instrumentation/magnetic-compass" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Magnetic Compass" },
|
|
"/instrumentation/nav[0]/gs" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Nav 1 Glideslope" },
|
|
"/instrumentation/nav[0]/cdi" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Nav 1 CDI" },
|
|
"/instrumentation/nav[1]/gs" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Nav 2 Glideslope" },
|
|
"/instrumentation/nav[1]/cdi" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Nav 2 CDI" },
|
|
"/instrumentation/slip-skid-ball" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Slip/Skid Ball" },
|
|
"/instrumentation/turn-indicator" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Turn Indicator" },
|
|
"/instrumentation/vertical-speed-indicator" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "VSI" },
|
|
"/systems/electrical" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Electrical system" },
|
|
"/systems/pitot" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Pitot system" },
|
|
"/systems/static" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Static system" },
|
|
"/systems/vacuum" : { type: type.MTBF, failure: fail.SERVICEABLE, desc: "Vacuum system" },
|
|
"/controls/gear/gear-down" : { type: type.MCBF, failure: "/gear/serviceable", desc: "Gear" },
|
|
"/controls/flight/aileron" : { type: type.MTBF, failure: fail.JAM, desc: "Aileron" },
|
|
"/controls/flight/elevator" : { type: type.MTBF, failure: fail.JAM, desc: "Elevator" },
|
|
"/controls/flight/rudder" : { type: type.MTBF, failure: fail.JAM, desc: "Rudder" },
|
|
"/controls/flight/flaps" : { type: type.MCBF, failure: fail.JAM, desc: "Flaps" },
|
|
"/controls/flight/speedbrake" : { type: type.MCBF, failure: fail.JAM, desc: "Speed Brake" }
|
|
};
|
|
|
|
# Return the failure entry for a given property
|
|
var getFailure = func (prop) {
|
|
var o = breakHash[prop];
|
|
|
|
if (o.failure == fail.SERVICEABLE) {
|
|
return prop ~ "/serviceable";
|
|
} elsif (o.failure == fail.ENGINE) {
|
|
return failure_root ~ prop ~ "/serviceable";
|
|
} elsif (o.failure == fail.JAM) {
|
|
return failure_root ~ prop ~ "/serviceable";
|
|
} else {
|
|
return o.failure;
|
|
}
|
|
}
|
|
|
|
# Fail a given property, either using a serviceable flag, or by jamming the property
|
|
var failProp = func(prop) {
|
|
|
|
var o = breakHash[prop];
|
|
var p = getFailure(prop);
|
|
|
|
if (getprop(p) == 1) {
|
|
setprop(p, 0);
|
|
|
|
# We always print to the console
|
|
print(getprop("/sim/time/gmt-string") ~ " : " ~ o.desc ~ " failed");
|
|
|
|
if (getprop(failure_root ~ "/display-on-screen")) {
|
|
# Display message to the screen in red
|
|
screen.log.write(o.desc ~ " failed", 1.0, 0.0, 0.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
# Unfail a given property, used for resetting a failure state.
|
|
var unfailProp = func(prop)
|
|
{
|
|
var p = getFailure(prop);
|
|
setprop(p, 1);
|
|
}
|
|
|
|
# Unfail all the failed properties
|
|
var unfail = func {
|
|
foreach(var prop; keys(breakHash)) {
|
|
unfailProp(prop);
|
|
}
|
|
}
|
|
|
|
# Listener to jam a property. Note that the property to jam is
|
|
# encoded within the property name
|
|
var jamListener = func(p) {
|
|
var jamprop = string.replace(p.getParent().getPath(), failure_root, "");
|
|
#jamprop = string.replace(jamprop, "/serviceable", "");
|
|
var prop = props.globals.getNode(jamprop);
|
|
if (p.getValue()) {
|
|
prop.setAttribute("writable", 1);
|
|
} else {
|
|
prop.setAttribute("writable", 0);
|
|
}
|
|
}
|
|
|
|
# Listener for an engine property. Note that the engine to set is
|
|
# encoded within the property name. We set both the magnetos and
|
|
# cutoff to handle different engine models.
|
|
var engineListener = func(p) {
|
|
var e = string.replace(p.getParent().getPath(), failure_root, "");
|
|
var prop = props.globals.getNode(e);
|
|
if (p.getValue()) {
|
|
# Enable the properties, but don't set the magnetos, as they may
|
|
# be off for a reason.
|
|
var magnetos = props.globals.getNode("/controls/" ~ e ~ "/magnetos", 1);
|
|
var cutoff = props.globals.getNode("/controls/" ~ e ~ "/cutoff", 1);
|
|
magnetos.setAttribute("writable", 1);
|
|
cutoff.setAttribute("writable", 1);
|
|
cutoff.setValue(0);
|
|
} else {
|
|
# Switch off the engine, and disable writing to it.
|
|
var magnetos = props.globals.getNode("/controls/" ~ e ~ "/magnetos", 1);
|
|
var cutoff = props.globals.getNode("/controls/" ~ e ~ "/cutoff", 1);
|
|
magnetos.setValue(0);
|
|
cutoff.setValue(1);
|
|
magnetos.setAttribute("writable", 0);
|
|
cutoff.setAttribute("writable", 0);
|
|
}
|
|
}
|
|
|
|
# Perform a MCBF check against a failure property.
|
|
var checkMCBF = func(prop) {
|
|
var mcbf = getprop(failure_root ~ prop.getPath() ~ "/mcbf");
|
|
# mcbf == mean cycles between failures
|
|
# hence 2*mcbf is the number of _half-cycles_ between failures,
|
|
# which is relevant because we do this check on each half-cycle:
|
|
if ((mcbf > 0) and !int(2 * mcbf * rand())) {
|
|
# Get the property information.
|
|
failProp(prop.getPath());
|
|
}
|
|
}
|
|
|
|
# Timer based loop to check MTBF properties
|
|
var checkMTBF = func {
|
|
foreach(var prop; keys(breakHash)) {
|
|
var o = breakHash[prop];
|
|
if (o.type == type.MTBF) {
|
|
var mtbf = getprop(failure_root ~ prop ~ "/mtbf");
|
|
if (mtbf and !int(rand() * mtbf / dt)) {
|
|
failProp(prop);
|
|
}
|
|
}
|
|
}
|
|
settimer(checkMTBF, dt);
|
|
}
|
|
|
|
# Function to set all MTBF failures to a give value. Mainly for testing.
|
|
var setAllMTBF = func(mtbf) {
|
|
foreach(var prop; keys(breakHash)) {
|
|
var o = breakHash[prop];
|
|
if (o.type == type.MTBF) {
|
|
setprop(failure_root ~ prop ~ "/mtbf", mtbf);
|
|
}
|
|
}
|
|
}
|
|
|
|
# Function to set all MCBF failures to a give value. Mainly for testing.
|
|
var setAllMCBF = func(mcbf) {
|
|
foreach(var prop; keys(breakHash)) {
|
|
var o = breakHash[prop];
|
|
if (o.type == type.MCBF) {
|
|
setprop(failure_root ~ prop ~ "/mcbf", mcbf);
|
|
}
|
|
}
|
|
}
|
|
|
|
# Initialization, called once Nasal and the FDM are loaded properly.
|
|
var fdm_init_listener = _setlistener("/sim/signals/fdm-initialized", func {
|
|
removelistener(fdm_init_listener); # uninstall, so we're only called once
|
|
srand();
|
|
|
|
# Engines are added dynamically because there may be an arbitrary number
|
|
var i = 1;
|
|
foreach (var e; props.globals.getNode("/engines").getChildren("engine")) {
|
|
breakHash[e.getPath()] = { type: type.MTBF, failure: fail.ENGINE, desc : "Engine " ~ i };
|
|
i = i+1;
|
|
}
|
|
|
|
# Set up serviceable, MCBF and MTBF properties.
|
|
foreach(var prop; keys(breakHash)) {
|
|
var o = breakHash[prop];
|
|
var t = "/mcbf";
|
|
|
|
if (o.type == type.MTBF) {
|
|
t = "/mtbf";
|
|
}
|
|
|
|
# Set up the MTBF/MCFB properties to 0. Note that they are in a separate
|
|
# subtree, as there's no guarantee that the property isn't a leaf.
|
|
props.globals.initNode(failure_root ~ prop ~ t, 0);
|
|
|
|
if (o.failure == fail.SERVICEABLE) {
|
|
# If the property has a serviceable property, set it if appropriate.
|
|
props.globals.initNode(prop ~ "/serviceable", 1, "BOOL");
|
|
} elsif (o.failure == fail.JAM) {
|
|
# In the JAM case, we actually have a dummy serviceable property for the GUI.
|
|
props.globals.initNode(failure_root ~ prop ~ "/serviceable", 1, "BOOL");
|
|
setlistener(failure_root ~ prop ~ "/serviceable", jamListener);
|
|
} elsif (o.failure == fail.ENGINE) {
|
|
# In the JAM case, we actually have a dummy serviceable property for the GUI.
|
|
props.globals.initNode(failure_root ~ prop ~ "/serviceable", 1, "BOOL");
|
|
setlistener(failure_root ~ prop ~ "/serviceable", engineListener);
|
|
} else {
|
|
# If the serviceable property is actually defined, check it is set.
|
|
props.globals.initNode(o.failure, 1, "BOOL");
|
|
}
|
|
|
|
if (o.type == type.MCBF) {
|
|
# Set up listener for MCBF properties, only when the value changes.
|
|
setlistener(prop, checkMCBF, 0, 0);
|
|
}
|
|
}
|
|
|
|
# Start checking for failures.
|
|
checkMTBF();
|
|
});
|
|
|