// Pseudo-code // TODO: add link to the ports of the boundary // TODO: update MM to reflect the new changes to the structure (everything is an Activity with a Next link, and everything has a duration) // init_place = new_place() // branches = [(init_place, [(0, topmost_model)]] // while branches: // prev, options = branches.pop() // nr = find_min_time(options) // time, cur = options.pop(nr) // // if type(cur) == "Event": // // Just add the current node and augment the duration for the next one // prev_model = new_element(cur) // if (cur.next != None): // options.append((time + cur.next.duration, cur.next)) // branches.append((prev_model, options)) // else: // // recurse upwards until we can follow a link // elem = containee(cur) // while elem.next_activity == None and containee(elem) != None: // elem = containee(elem) // if containee(elem) == None: // // finished this branch // continue! // else: // cur = elem.next_activity // options.append((time + elem.duration, cur.next)) // branches.append((prev_model, options)) // elif type(cur) == "Sequence": // options.append((time + first.duration, cur.first)) // branches.append((prev_model, options)) // elif type(cur) == "Parallel": // // Add all starts of the parallel as potential next one // // But keep the previous source, as we only expanded // for next in cur.start_nodes: // options.append((time + next.duration, next)) // branches.append((prev, options)) // elif type(cur) == "Alternative": // // Expand into new branches, but keep the options as is (add the individual options though) // for next in cur.start_nodes: // options.append((time + next.duration, next)) // // Don't rewrite the source, as we effectively want to branch out from this node // branches.append(prev, options) include "primitives.alh" include "modelling.alh" include "object_operations.alh" Element function env_to_EPN(params : Element, output_mms : Element): Element result Element out_model Element in_model String init_place String current_activity Element branches Element options Element branch String previous_place Integer i Integer cnt Integer min_time Integer index_min_time Element option Integer current_time String new_transition String new_model Element containers Element new_options Element inner_elements Element entry String type String prev_model String element result = create_node() out_model = instantiate_model(output_mms["Encapsulated_PetriNet"]) in_model = params["environment"] // Create the initial place init_place = instantiate_node(out_model, "Place", "") instantiate_attribute(out_model, init_place, "tokens", 1) // Set current element to the TopActivity, which will be expanded current_activity = set_pop(allInstances(in_model, "TopActivity")) // Initialize the data structure with the current element and initial place branches = create_node() options = create_node() list_append(options, create_tuple(0, current_activity)) set_add(branches, create_tuple(init_place, options)) // Keep going as long as there are branches to resolve while (read_nr_out(branches) > 0): // Still a branch, so pick one at random branch = set_pop(branches) previous_place = branch[0] options = branch[1] // Find the index of the option with the lowest time (first element of tuple) i = 0 cnt = list_len(options) min_time = 9999999 index_min_time = -1 while (i < cnt): entry = list_read(options, i) if (integer_lt(entry[0], min_time)): min_time = entry[0] index_min_time = i i = i + 1 // Pop the minimal option option = list_pop(options, index_min_time) current_time = option[0] current_activity = option[1] // Figure out the type type = read_type(in_model, current_activity) // Now branch based on the type if (type == "Event"): // Process an event: update the PN and go to the next activity new_transition = instantiate_node(out_model, "Transition", "") new_model = instantiate_node(out_model, "Place", "") instantiate_link(out_model, "P2T", "", prev_model, new_transition) instantiate_link(out_model, "T2P", "", new_transition, new_model) // Check if there is a Next to this Event, meaning another event if (read_nr_out(allOutgoingAssociationInstances(in_model, current_activity, "Next")) > 0): // We have a Next, so just push that next event on the options current_activity = set_pop(allAssociationDestinations(in_model, current_activity, "Next")) list_append(options, create_tuple(integer_addition(current_time, read_attribute(in_model, current_activity, "duration")), current_activity)) set_add(branches, create_tuple(new_model, options)) else: // No Next in this node, so we recurse up until one of these elements does have a next (or we reach the top) while (read_nr_out(allOutgoingAssociationInstances(in_model, current_activity, "Next")) == 0): // Recurse up containers = allAssociationOrigins(in_model, current_activity, "Contains") if (read_nr_out(containers) == 1): current_activity = set_pop(containers) elif (read_nr_out(containers) == 0): // No more containers, so at top element break! if (read_nr_out(containers) == 0): // Nothing left to do, so clear up this branch, but continue with the others continue! else: // Found a node with a Next link: we follow it current_activity = set_pop(allAssociationDestinations(in_model, current_activity, "Next")) list_append(options, create_tuple(integer_addition(current_time, read_attribute(in_model, current_activity, "duration")), current_activity)) set_add(branches, create_tuple(new_model, options)) elif (type == "Sequence"): // Process a sequence: just move the current option to the enclosing activity inner_elements = allAssociationDestinations(in_model, current_activity, "Contains") while (read_nr_out(inner_elements) > 0): element = set_pop(inner_elements) if (read_nr_out(allIncomingAssociationInstances(in_model, element, "Next")) == 0): current_activity = element break! // current_activity now contains the inner element to execute list_append(options, create_tuple(integer_addition(current_time, read_attribute(in_model, current_activity, "duration")), current_activity)) // keep the current branch alive, as everything is updated by reference set_add(branches, branch) elif (type == "Parallel"): // Process a parallel: create new options for each containing element inner_elements = allAssociationDestinations(in_model, current_activity, "Contains") while (read_nr_out(inner_elements) > 0): current_activity = set_pop(inner_elements) // current_activity now contains the inner element to execute in parallel (an option) list_append(options, create_tuple(integer_addition(current_time, read_attribute(in_model, current_activity, "duration")), current_activity)) // keep the current branch alive, as everything is updated by reference set_add(branches, branch) elif (type == "Alternative"): inner_elements = allAssociationDestinations(in_model, current_activity, "Contains") while (read_nr_out(inner_elements) > 0): current_activity = set_pop(inner_elements) // current_activity now contains the inner element to execute in alternative branches (a branch) new_options = set_copy(options) list_append(options, create_tuple(integer_addition(current_time, read_attribute(in_model, current_activity, "duration")), current_activity)) set_add(branches, create_tuple(previous_place, new_options)) dict_add(result, "Encapsulated_PetriNet", out_model) return result!