c108f3b988
- Fix: runtime exception in remove_failure_mode() - Fix: keep failure & trigger status on teleport. - Fix: allow random failures from the gui to be enabled/disabled multiple times. - Fix: mcbf/mtbf are set to zero when they fire, so they can be reactivated from the gui. - Fix: string casts of several trigger types had syntax errors. - Usability: screen messages related to failures now use positive logic: "condition 100%" instead of "failure level 0%" - Performance: Time triggers now use internal timers, instead of requiring being polled. - Reviewed Trigger interface for more rational usage. reset() is replaced by arm()/disarm() - Added a subscription interface to listen to FailureMgr events. - Added an internal log buffer to keep a record of relevant events and present them to gui elements. - Several usability improvements to the FailureMgr Nasal API.
332 lines
9.3 KiB
Text
332 lines
9.3 KiB
Text
# Failure Manager public interface
|
|
#
|
|
# Copyright (C) 2014 Anton Gomez Alvedro
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License as
|
|
# published by the Free Software Foundation; either version 2 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
|
var proproot = "sim/failure-manager/";
|
|
|
|
|
|
##
|
|
# Nasal modules can subscribe to FailureMgr events.
|
|
# Each event has an independent dispatcher so modules can subscribe only to
|
|
# the events they are interested in. This also simplifies processing at client
|
|
# side by being able to subscibe different callbacks to different events.
|
|
#
|
|
# Example:
|
|
#
|
|
# var handle = FailureMgr.events["trigger-fired"].subscribe(my_callback);
|
|
|
|
var events = {};
|
|
|
|
# Event: trigger-fired
|
|
# Format: { mode_id: <failure mode id>, trigger: <trigger that fired> }
|
|
events["trigger-fired"] = globals.events.EventDispatcher.new();
|
|
|
|
##
|
|
# Encodes a pair "category" and "failure_mode" into a "mode_id".
|
|
#
|
|
# These just have the simple form "category/mode", and are used to refer to
|
|
# failure modes throughout the FailureMgr API and to create a path within the
|
|
# sim/failure-manager property tree for the failure mode.
|
|
#
|
|
# examples of categories:
|
|
# structural, instrumentation, controls, sensors, etc...
|
|
#
|
|
# examples of failure modes:
|
|
# altimeter, pitot, left-tire, landing-light, etc...
|
|
|
|
var get_id = func(category, failure_mode) {
|
|
return sprintf("%s/%s", string.normpath(category), failure_mode);
|
|
}
|
|
|
|
##
|
|
# Returns a vector containing: [category, failure_mode]
|
|
|
|
var split_id = func(mode_id) {
|
|
return [string.normpath(io.dirname(mode_id)), io.basename(mode_id)];
|
|
}
|
|
|
|
##
|
|
# Subscribe a new failure mode to the system.
|
|
#
|
|
# id: Unique identifier for this failure mode.
|
|
# eg: "engine/carburetor-ice"
|
|
#
|
|
# description: Short text description, suitable for printing to the user.
|
|
# eg: "Ice in the carburetor"
|
|
#
|
|
# actuator: Object implementing the FailureActuator interface.
|
|
# Used by the failure manager to apply a certain level of
|
|
# failure to the failure mode.
|
|
|
|
var add_failure_mode = func(id, description, actuator) {
|
|
_failmgr.add_failure_mode(
|
|
FailureMode.new(id, description, actuator));
|
|
}
|
|
|
|
##
|
|
# Returns a vector with all failure modes in the system.
|
|
# Each vector entry is a hash with the following keys:
|
|
# { id, description }
|
|
|
|
var get_failure_modes = func() {
|
|
_failmgr.get_failure_modes();
|
|
}
|
|
|
|
##
|
|
# Remove a failure mode from the system.
|
|
# id: FailureMode id string, e.g. "systems/pitot"
|
|
|
|
var remove_failure_mode = func(id) {
|
|
_failmgr.remove_failure_mode(id);
|
|
}
|
|
|
|
##
|
|
# Removes all failure modes from the failure manager.
|
|
|
|
var remove_all = func {
|
|
_failmgr.remove_all();
|
|
}
|
|
|
|
##
|
|
# Attaches a trigger to the given failure mode. Discards the current trigger
|
|
# if any.
|
|
#
|
|
# mode_id: FailureMode id string, e.g. "systems/pitot"
|
|
# trigger: Trigger object or nil. Nil will just detach the current trigger
|
|
|
|
var set_trigger = func(mode_id, trigger) {
|
|
_failmgr.set_trigger(mode_id, trigger);
|
|
}
|
|
|
|
##
|
|
# Returns the trigger object attached to the given failure mode.
|
|
# mode_id: FailureMode id string, e.g. "systems/pitot"
|
|
|
|
var get_trigger = func(mode_id) {
|
|
_failmgr.get_trigger(mode_id);
|
|
}
|
|
|
|
##
|
|
# Applies a certain level of failure to this failure mode.
|
|
#
|
|
# mode_id: Failure mode id string.
|
|
# level: Floating point number in the range [0, 1]
|
|
# Zero represents no failure and one means total failure.
|
|
|
|
var set_failure_level = func(mode_id, level) {
|
|
setprop(proproot ~ mode_id ~ "/failure-level", level);
|
|
}
|
|
|
|
##
|
|
# Returns the current failure level for the given failure mode.
|
|
# mode_id: Failure mode id string.
|
|
|
|
var get_failure_level = func(mode_id) {
|
|
getprop(proproot ~ mode_id ~ "/failure-level");
|
|
}
|
|
|
|
##
|
|
# Restores all failure modes to level = 0
|
|
|
|
var repair_all = func {
|
|
_failmgr.repair_all();
|
|
}
|
|
|
|
##
|
|
# Returns a vector of timestamped failure manager events, such as the
|
|
# messages shown in the console when there are changes to the failure conditions.
|
|
#
|
|
# Each entry in the vector has the following format:
|
|
# { time: <time stamp>, message: <event description> }
|
|
|
|
var get_log_buffer = func {
|
|
_failmgr.logbuf.get_buffer();
|
|
}
|
|
|
|
##
|
|
# Allows applications to disable the failure manager and restore it later on.
|
|
# While disabled, no failure modes will be activated from the failure manager.
|
|
|
|
var enable = func setprop(proproot ~ "enabled", 1);
|
|
var disable = func setprop(proproot ~ "enabled", 0);
|
|
|
|
##
|
|
# Encapsulates a condition that when met, will make the failure manager to
|
|
# apply a certain level of failure to the failure mode it is bound to.
|
|
#
|
|
# Two types of triggers are supported: pollable and asynchronous.
|
|
#
|
|
# Pollable triggers require periodic check for trigger conditions. For example,
|
|
# an altitude trigger will need to poll current altitude until the fire
|
|
# condition is reached.
|
|
#
|
|
# Asynchronous trigger do not require periodic updates. They can detect
|
|
# the firing condition by themselves by using timers or listeners.
|
|
# Async triggers must call the inherited method on_fire() to let the Failure
|
|
# Manager know about the fired condition.
|
|
#
|
|
# See Aircraft/Generic/Systems/failures.nas for concrete examples of triggers.
|
|
|
|
var Trigger = {
|
|
|
|
type: nil,
|
|
# 1 for pollable triggers, 0 for async triggers.
|
|
requires_polling: 0,
|
|
enabled: 0,
|
|
|
|
new: func {
|
|
return {
|
|
parents: [Trigger],
|
|
params: {},
|
|
armed: 0,
|
|
fired: 0,
|
|
|
|
##
|
|
# Async triggers shall call the on_fire() callback when their fire
|
|
# conditions are met to notify the failure manager.
|
|
on_fire: func 0,
|
|
|
|
_path: nil
|
|
};
|
|
},
|
|
|
|
##
|
|
# Forces a check of the firing conditions. Returns 1 if the trigger fired,
|
|
# 0 otherwise.
|
|
|
|
update: func 0,
|
|
|
|
##
|
|
# Returns a printable string describing the trigger condition.
|
|
|
|
to_str: func "undefined trigger",
|
|
|
|
##
|
|
# Modify a trigger parameter. Parameters will take effect after the next
|
|
# call to reset()
|
|
|
|
set_param: func(param, value) {
|
|
assert(me._path != nil, "Trigger.set_param: unbound trigger");
|
|
|
|
contains(me.params, param) or
|
|
die("Trigger.set_param: undefined param: " ~ param);
|
|
|
|
setprop(sprintf("%s/%s",me._path, param), value);
|
|
},
|
|
|
|
##
|
|
# Load trigger parameters and reset internal state. Once armed, the trigger
|
|
# will fire as soon as the right conditions are met. It can be called after
|
|
# the trigger fires to rearm it again.
|
|
#
|
|
# The "armed" condition survives enable/disable calls.
|
|
|
|
arm: func {
|
|
assert(me._path != nil, "Trigger.arm: unbound trigger");
|
|
setprop(me._path ~ "/armed", 1);
|
|
},
|
|
|
|
_arm: func {
|
|
foreach (var p; keys(me.params))
|
|
me.params[p] = getprop(sprintf("%s/%s", me._path, p));
|
|
|
|
me.fired = 0;
|
|
me.armed = 1;
|
|
},
|
|
|
|
##
|
|
# Deactivate the trigger. The trigger will not fire until rearmed again.
|
|
|
|
disarm: func {
|
|
assert(me._path != nil, "Trigger.disarm: unbound trigger");
|
|
setprop(me._path ~ "/armed", 0);
|
|
},
|
|
|
|
_disarm: func {
|
|
me.armed = 0;
|
|
},
|
|
|
|
##
|
|
# Enables/disables the trigger. While a trigger is disabled, any timer
|
|
# or listener that could potentially own shall be disabled.
|
|
#
|
|
# The FailureMgr calls these methods when the entire system is
|
|
# enabled/disabled. By keeping enabled/disabled state separated from
|
|
# armed/disarmed allows the FailureMgr to keep its configuration while
|
|
# disabled, i.e. those triggers that where armed when the system was
|
|
# disabled will resume when the system is enabled again.
|
|
#
|
|
# The FailureMgr disables itself during a teleport.
|
|
|
|
enable: func { me.enabled = 1 },
|
|
disable: func { me.enabled = 0 },
|
|
|
|
##
|
|
# Creates an interface for the trigger in the property tree.
|
|
# Every parameter in the params hash will be exposed, in addition to
|
|
# a path/reset property for resetting the trigger from the prop tree.
|
|
|
|
bind: func(path) {
|
|
assert(me._path == nil, "Trigger.bind: trigger already bound");
|
|
|
|
me._path = path;
|
|
props.globals.getNode(path) != nil or props.globals.initNode(path);
|
|
props.globals.getNode(path).setValues(me.params);
|
|
|
|
var prop = path ~ "/armed";
|
|
props.globals.initNode(prop, 0, "BOOL");
|
|
setlistener(prop,
|
|
func(p) { p.getValue() ? me._arm() : me._disarm() }, 0, 1);
|
|
},
|
|
|
|
##
|
|
# Removes this trigger's interface from the property tree.
|
|
|
|
unbind: func {
|
|
props.globals.getNode(me._path ~ "/armed").remove();
|
|
foreach (var p; keys(me.params))
|
|
props.globals.getNode(me._path ~ "/" ~ p).remove();
|
|
|
|
me._path = nil;
|
|
}
|
|
};
|
|
|
|
##
|
|
# FailureActuators encapsulate the actions required for activating the actual
|
|
# failure simulation.
|
|
#
|
|
# Traditionally this action was just manipulating a "serviceable" property
|
|
# somewhere, but the FailureActuator gives you more flexibility, allowing you
|
|
# to touch several properties at once or call other Nasal scripts, for example.
|
|
#
|
|
# See Aircraft/Generic/Systems/failure.nas and
|
|
# Aircraft/Generic/Systems/compat_failures.nas for some examples of actuators.
|
|
|
|
var FailureActuator = {
|
|
|
|
##
|
|
# Called from the failure manager to activate a certain level of failure.
|
|
# level: Target level of failure [0 to 1].
|
|
|
|
set_failure_level: func(level) 0,
|
|
|
|
##
|
|
# Returns the level of failure that is currently being simulated.
|
|
|
|
get_failure_level: func 0,
|
|
};
|