示例#1
0
        public static UnitStateChangeTransaction CreateDeactivationTransaction(Unit unit)
        {
            var transaction = new UnitStateChangeTransaction(unit)
            {
                Name = $"Deactivate {unit.UnitName}", Lock = GlobalTransactionLock
            };

            var units_to_deactivate = RequirementDependencies.TraverseDependencyGraph(unit.UnitName,
                                                                                      t => t.RequirementType == RequirementDependencyType.BindsTo ||
                                                                                      t.RequirementType == RequirementDependencyType.Requires ||
                                                                                      t.RequirementType == RequirementDependencyType.PartOf).SelectMany(dep =>
            {
                var ret = new[] { dep.LeftUnit, dep.RightUnit };

                if (dep.RequirementType == RequirementDependencyType.PartOf) // PartOf is a one way dependency
                {
                    if (unit.UnitName != dep.RightUnit)                      // only when stopping the "right hand" side of a PartOf dependency should the action be propagated
                    {
                        return(new string[0]);
                    }
                }

                foreach (var unit_name in ret)
                {
                    var u = GetUnit(unit_name);

                    if (u == null)
                    {
                        continue;
                    }

                    if (!transaction.Reasoning.ContainsKey(u))
                    {
                        transaction.Reasoning[u] = new List <string>();
                    }

                    transaction.Reasoning[u].Add($"Deactivating {u.UnitName} because of dependency {dep}");
                }

                return(ret);
            })
                                      .Select(GetUnit).ToList();

            units_to_deactivate.Add(unit);
            units_to_deactivate = units_to_deactivate.Distinct().ToList();

            var order_graph = OrderingDependencies.TraverseDependencyGraph(unit.UnitName, t => units_to_deactivate.Any(u => u.UnitName == t.LeftUnit || u.UnitName == t.RightUnit), true).ToList();

            var new_order     = new List <Unit>();
            var initial_nodes = order_graph.Where(dependency => !order_graph.Any(d => dependency.LeftUnit == d.RightUnit)).Select(t => t.LeftUnit).Distinct().ToList(); // find the "first" nodes

            if (!initial_nodes.Any() && order_graph.Any())
            {
                // possible dependency loop
                throw new Exception($"Failed to order dependent units while preparing the deactivation transaction for {unit.UnitName}.");
            }
            else if (!initial_nodes.Any())
            {
                new_order = units_to_deactivate;
            }
            else
            {
                var processed_vertices = new List <string>();

                while (initial_nodes.Any())
                {
                    var dep = initial_nodes.First();
                    initial_nodes.Remove(dep);
                    new_order.Add(GetUnit(dep));

                    var other_edges = order_graph.Where(d => d.LeftUnit == dep).ToList();
                    other_edges.ForEach(edge => order_graph.Remove(edge));
                    var edges_to_add = other_edges.Where(edge => { var m = edge.RightUnit; return(!order_graph.Any(e => e.RightUnit == m && !processed_vertices.Contains(e.LeftUnit))); }).Select(t => t.RightUnit);

                    initial_nodes.AddRange(edges_to_add);
                }

                // prune the new order down to only units we've decided to deactivate, then append the unordered units not included in the new order
                new_order = new_order.Where(units_to_deactivate.Contains).Concat(units_to_deactivate.Where(u => !new_order.Contains(u)).ToList()).ToList();

                // check the new order against the rules
                bool satisfied = true;

                foreach (var order_rule in order_graph)
                {
                    var index_1 = new_order.FindIndex(u => u.UnitName == order_rule.LeftUnit);
                    var index_2 = new_order.FindIndex(u => u.UnitName == order_rule.RightUnit);

                    if (index_1 == -1 || index_2 == -1) // one of the vertices got pruned
                    {
                        continue;
                    }

                    if (index_1 > index_2) // now in reverse
                    {
                        satisfied = false;
                        break;
                    }
                }

                if (!satisfied)
                {
                    throw new Exception($"Unsatisfiable set of ordering rules encountered when building the deactivation transaction for unit {unit.UnitName}.");
                }
            }

            units_to_deactivate = new_order;

            // build the transaction

            foreach (var sub_unit in units_to_deactivate)
            {
                transaction.AffectedUnits.Add(sub_unit);
                transaction.Add(sub_unit.GetDeactivationTransaction());
            }

            return(transaction);
        }
示例#2
0
        public static UnitStateChangeTransaction CreateActivationTransaction(Unit unit)
        {
            var transaction = new UnitStateChangeTransaction(unit)
            {
                Name = $"Activate {unit.UnitName}", Lock = GlobalTransactionLock
            };
            var unit_list = new List <Unit>()
            {
                unit
            };

            var ignore_conflict_deactivation_failure = new Dictionary <string, bool>();
            var fail_if_unstarted = new Dictionary <string, bool>();
            var ignore_failure    = new Dictionary <string, bool>()
            {
                { unit.UnitName, false }
            };
            var req_graph = RequirementDependencies.TraverseDependencyGraph(unit.UnitName, t => t.RequirementType != RequirementDependencyType.Conflicts && t.RequirementType != RequirementDependencyType.PartOf, false);

            // list all units to be started
            foreach (var dependency in req_graph)
            {
                var parent = dependency.LeftUnit;
                var child  = dependency.RightUnit;

                var target_unit = GetUnit(child);

                if (target_unit == null)
                {
                    continue;
                }

                if (!unit_list.Contains(target_unit))
                {
                    unit_list.Add(target_unit);
                }

                if (!transaction.Reasoning.ContainsKey(target_unit))
                {
                    transaction.Reasoning[target_unit] = new List <string>();
                }

                transaction.Reasoning[target_unit].Add($"Activating {target_unit.UnitName} because of dependency {dependency}");
            }

            // determine whether the failure of each unit activation makes the entire transaction fail
            string current_unit = unit.UnitName;
            var    list         = new List <RequirementDependency>();

            while (true)
            {
                var dependencies_to_resolve = req_graph.Where(dep => dep.LeftUnit == current_unit).ToList();

                foreach (var dependency in dependencies_to_resolve)
                {
                    if (dependency.RequirementType == RequirementDependencyType.Wants)
                    {
                        ignore_failure[dependency.RightUnit] = ignore_failure.ContainsKey(dependency.RightUnit) ? ignore_failure[dependency.RightUnit] : true;
                    }
                    else
                    {
                        ignore_failure[dependency.RightUnit] = false;
                        list.Add(dependency);
                    }
                }

                if (!list.Any())
                {
                    break;
                }

                current_unit = list.First().RightUnit;
                list.RemoveAt(0);
            }

            // determine whether each unit is actually to be started or not (Requisite only checks whether the unit is active)
            fail_if_unstarted = unit_list.ToDictionary(u => u.UnitName, u => RequirementDependencies.GetDependencies(u.UnitName).All(dep => dep.RequirementType == RequirementDependencyType.Requisite));
            fail_if_unstarted[unit.UnitName] = false; // the unit we're set out to start isn't subject to this

            // create unit ordering according to ordering dependencies
            var order_graph = OrderingDependencies.TraverseDependencyGraph(unit.UnitName, t => true, true).ToList();

            var new_order              = new List <Unit>();
            var initial_nodes          = order_graph.Where(dependency => !order_graph.Any(d => dependency.LeftUnit == d.RightUnit));
            var initial_nodes_filtered = initial_nodes.Where(dependency => unit_list.Any(u => dependency.LeftUnit == u.UnitName || dependency.RightUnit == u.UnitName));
            var selected_nodes         = initial_nodes_filtered.Select(t => t.LeftUnit).Distinct().ToList(); // find the "first" nodes

            if (!initial_nodes_filtered.Any() && !initial_nodes.Any() && order_graph.Any())
            {
                // possible dependency loop
                throw new Exception($"Failed to order dependent units while preparing the activation transaction for {unit.UnitName}.");
            }
            else if (!initial_nodes_filtered.Any())
            {
                new_order = unit_list;
            }
            else
            {
                var processed_vertices = new List <string>();

                while (selected_nodes.Any())
                {
                    var dep = selected_nodes.First();
                    selected_nodes.Remove(dep);

                    var dep_unit = GetUnit(dep);

                    if (dep_unit == null)
                    {
                        if (!ignore_failure.ContainsKey(dep) || ignore_failure[dep])
                        {
                            continue;
                        }
                        else
                        {
                            throw new Exception($"Couldn't find required unit {dep}");
                        }
                    }

                    new_order.Add(GetUnit(dep));

                    var other_edges = order_graph.Where(d => d.LeftUnit == dep).ToList();
                    other_edges.ForEach(edge => order_graph.Remove(edge));
                    var edges_to_add = other_edges.Where(edge => { var m = edge.RightUnit; return(!order_graph.Any(e => e.RightUnit == m && !processed_vertices.Contains(e.LeftUnit))); }).Select(t => t.RightUnit);

                    selected_nodes.AddRange(edges_to_add);
                }

                new_order.Reverse();
                new_order = new_order.Concat(unit_list.Where(u => !new_order.Contains(u)).ToList()).ToList();

                // check the new order against the rules
                bool satisfied = true;

                foreach (var order_rule in order_graph)
                {
                    var index_1 = new_order.FindIndex(u => u.UnitName == order_rule.LeftUnit);
                    var index_2 = new_order.FindIndex(u => u.UnitName == order_rule.RightUnit);

                    if (index_1 < index_2)
                    {
                        satisfied = false;
                        break;
                    }
                }

                if (!satisfied)
                {
                    throw new Exception($"Unsatisfiable set of ordering rules encountered when building the activation transaction for unit {unit.UnitName}.");
                }
            }

            unit_list = new_order;

            // get a list of units to stop
            var conflicts     = unit_list.SelectMany(u => RequirementDependencies.GetDependencies(u.UnitName).Where(d => d.RequirementType == RequirementDependencyType.Conflicts));
            var units_to_stop = new List <Unit>();

            foreach (var conflicting_dep in conflicts)
            {
                var left  = GetUnit(conflicting_dep.LeftUnit);
                var right = GetUnit(conflicting_dep.RightUnit);

                Unit unit_to_stop = null;

                if (unit_list.Contains(left) && unit_list.Contains(right)) // conflict inside transaction
                {
                    var left_ignorable  = !ignore_failure.ContainsKey(left.UnitName) || ignore_failure[left.UnitName];
                    var right_ignorable = !ignore_failure.ContainsKey(right.UnitName) || ignore_failure[right.UnitName];

                    if (!left_ignorable && !right_ignorable)
                    {
                        throw new Exception($"Units {left.UnitName} and {right.UnitName} conflict because of dependency {conflicting_dep}");
                    }

                    if (left_ignorable && !units_to_stop.Contains(left))
                    {
                        unit_to_stop = left;
                    }

                    if (right_ignorable && !units_to_stop.Contains(right))
                    {
                        unit_to_stop = right;
                    }
                }
                else if (unit_list.Contains(left) && !units_to_stop.Contains(right))
                {
                    unit_to_stop = right;
                }
                else if (unit_list.Contains(right) && !units_to_stop.Contains(left))
                {
                    unit_to_stop = left;
                }
                else
                {
                    continue;
                }

                units_to_stop.Add(unit_to_stop);

                if (!transaction.Reasoning.ContainsKey(unit_to_stop))
                {
                    transaction.Reasoning[unit_to_stop] = new List <string>();
                }

                transaction.Reasoning[unit_to_stop].Add($"Deactivating {unit_to_stop.UnitName} because of dependency {conflicting_dep}");
            }

            ignore_conflict_deactivation_failure = units_to_stop.ToDictionary(u => u.UnitName,
                                                                              u => conflicts.All(conflict =>
            {
                var requesting_end = u.UnitName == conflict.LeftUnit ? conflict.RightUnit : conflict.LeftUnit;
                return(!ignore_failure.ContainsKey(requesting_end) || ignore_failure[requesting_end]);
            }));

            // actually create the transaction

            foreach (var sub_unit in units_to_stop)
            {
                var deactivation_transaction = CreateDeactivationTransaction(sub_unit);

                var wrapper = new Transaction();

                wrapper.Add(new CheckUnitStateTask(UnitState.Active, sub_unit.UnitName, true));
                wrapper.Add(deactivation_transaction);

                wrapper.ErrorHandlingMode = ignore_conflict_deactivation_failure[sub_unit.UnitName] ? TransactionErrorHandlingMode.Ignore : TransactionErrorHandlingMode.Fail;
                wrapper.Name = $"Check and deactivate {sub_unit.UnitName}";

                transaction.Add(wrapper);
            }

            foreach (var sub_unit in unit_list)
            {
                var activation_transaction = sub_unit.GetActivationTransaction();

                if (fail_if_unstarted.ContainsKey(sub_unit.UnitName) && fail_if_unstarted[sub_unit.UnitName])
                {
                    activation_transaction = new Transaction(new CheckUnitStateTask(UnitState.Active, sub_unit.UnitName));
                }

                if (ignore_failure.ContainsKey(sub_unit.UnitName) && !ignore_failure[sub_unit.UnitName])
                {
                    activation_transaction.ErrorHandlingMode = TransactionErrorHandlingMode.Fail;
                }
                else
                {
                    activation_transaction.ErrorHandlingMode = TransactionErrorHandlingMode.Ignore;
                }

                transaction.Tasks.Add(activation_transaction);
            }

            return(transaction);
        }