Add 'simplified' version of the FTG+PM++ formalism with operational semantics

This commit is contained in:
Inte Vleminckx 2025-06-03 16:17:37 +02:00
parent ced3edbd08
commit d00b9c25db
16 changed files with 1135 additions and 0 deletions

View file

@ -0,0 +1,2 @@
# Match the model
model:RAM_pm_Model

View file

@ -0,0 +1,7 @@
model:RAM_pm_Model
# Check if the model isn't already connected to a process trace
start_trace:RAM_pt_StartTrace
:RAM_pt_Starts (start_trace -> model)
end_trace:RAM_pt_EndTrace
:RAM_pt_Ends (end_trace -> model)

View file

@ -0,0 +1,12 @@
# Keep the left hand side
model:RAM_pm_Model
# Connect a process trace to it
start_trace:RAM_pt_StartTrace
starts:RAM_pt_Starts (start_trace -> model)
end_trace:RAM_pt_EndTrace
ends:RAM_pt_Ends (end_trace -> model)
# Connect the start with the end
:RAM_pt_IsFollowedBy (start_trace -> end_trace)

View file

@ -0,0 +1,49 @@
# When a control port is active and is connected to an activity, we want to execute the activity
# But, if the activity has input_and (input_or = False). It only can be activated if all its inputs are active
# Match the model
model:RAM_pm_Model
# Match the a python automated activity
py_activity:RAM_pm_PythonAutomatedActivity {
# Check if all connected ports are active in case of input_and
condition = ```
all_active = True
# Check for or / and
if not get_slot_value(this, "input_or"):
# Get all the ctrl in ports
for has_ctrl_in in get_outgoing(this, "pm_HasCtrlIn"):
c_in_state = get_source(get_incoming(get_target(has_ctrl_in), "pm_Of")[0])
# Check if the port is active or not
if not get_slot_value(c_in_state, "active"):
all_active = False
break
all_active
```;
} model_to_activity:RAM_pm_Owns (model -> py_activity)
# Match a control activity in port that is active
ctrl_in:RAM_pm_CtrlActivityIn
ctrl_in_state:RAM_pm_CtrlPortState {
RAM_active = `get_value(this)`;
}
state_to_port:RAM_pm_Of (ctrl_in_state -> ctrl_in)
# Match the activity link to the port
activity_to_port:RAM_pm_HasCtrlIn (py_activity -> ctrl_in)
# Match the end of the trace
end_trace:RAM_pt_EndTrace
ends:RAM_pt_Ends (end_trace -> model)
# Match the previous trace element before the end trace
prev_trace_element:RAM_pt_Event
followed_by:RAM_pt_IsFollowedBy (prev_trace_element -> end_trace)

View file

@ -0,0 +1,42 @@
model:RAM_pm_Model
py_activity:RAM_pm_PythonAutomatedActivity {
condition = ```
start_activity = create_object(None, "pt_StartActivity")
create_activity_links(odapi, start_activity, matched("prev_trace_element"), matched("ctrl_in"))
input_data = extract_input_data(odapi, this)
result = execute_activity(odapi, globals()["packages"], this, input_data)
if len(result) == 3:
status_code, output_data, input_used = result
else:
status_code, output_data, input_used = *result, None
if input_used:
handle_artefact(odapi, start_activity, "pt_Artefact", "pt_Consumes", get(input_used), input_data[input_used], direction="DataFlowOut")
end_activity = create_object(None, "pt_EndActivity")
ctrl_out = get(status_code)
create_activity_links(odapi, end_activity, start_activity, ctrl_out, end_trace=matched("end_trace"))
if output_data:
port, data = output_data
handle_artefact(odapi, end_activity, "pt_Artefact", "pt_Produces", get(port), data, direction="DataFlowIn")
update_control_states(odapi, this, ctrl_out)
```;
}
model_to_activity:RAM_pm_Owns
ctrl_in:RAM_pm_CtrlActivityIn
ctrl_in_state:RAM_pm_CtrlPortState {
RAM_active = `False`;
}
state_to_port:RAM_pm_Of (ctrl_in_state -> ctrl_in)
activity_to_port:RAM_pm_HasCtrlIn (py_activity -> ctrl_in)
end_trace:RAM_pt_EndTrace
ends:RAM_pt_Ends (end_trace -> model)
prev_trace_element:RAM_pt_Event

View file

@ -0,0 +1,36 @@
# When a control port is active and is connected to an activity, we want to execute the activity. If it is a composite one, we execute the inner workflow of it
# But, if the activity has input_and (input_or = False). It only can be activated if all its inputs are active
# Match the model
model:RAM_pm_Model
# Match the a python automated activity
activity:RAM_pm_Activity {
RAM_composite = `True`;
} model_to_activity:RAM_pm_Owns (model -> activity)
# Match a control activity in port that is active
ctrl_in:RAM_pm_CtrlActivityIn
ctrl_in_state:RAM_pm_CtrlPortState {
RAM_active = `get_value(this)`;
}
state_to_port:RAM_pm_Of (ctrl_in_state -> ctrl_in)
# Match the activity link to the port
activity_to_port:RAM_pm_HasCtrlIn (activity -> ctrl_in)
# Match the end of the trace
end_trace:RAM_pt_EndTrace
ends:RAM_pt_Ends (end_trace -> model)
# Match the previous trace element before the end trace
prev_trace_element:RAM_pt_Event
followed_by:RAM_pt_IsFollowedBy (prev_trace_element -> end_trace)

View file

@ -0,0 +1,29 @@
model:RAM_pm_Model
activity:RAM_pm_Activity {
RAM_composite = `True`;
condition = ```
# Execute inner workflow
execute_composite_workflow(odapi, this, matched("ctrl_in"), globals()["composite_linkage"], globals()["packages"], matched)
```;
}
model_to_activity:RAM_pm_Owns
ctrl_in:RAM_pm_CtrlActivityIn
ctrl_in_state:RAM_pm_CtrlPortState {
RAM_active = `False`;
}
state_to_port:RAM_pm_Of (ctrl_in_state -> ctrl_in)
activity_to_port:RAM_pm_HasCtrlIn (activity -> ctrl_in)
end_trace:RAM_pt_EndTrace
ends:RAM_pt_Ends (end_trace -> model)
prev_trace_element:RAM_pt_Event

View file

@ -0,0 +1,20 @@
# Match an active control output port
out_state:RAM_pm_CtrlPortState {
RAM_active = `get_value(this)`;
}
out:RAM_pm_CtrlOut
state_to_out:RAM_pm_Of (out_state -> out)
# Match an inactive control input port
in_state:RAM_pm_CtrlPortState {
RAM_active = `not get_value(this)`;
}
in:RAM_pm_CtrlIn
state_to_in:RAM_pm_Of (in_state -> in)
# Match the connection between those two ports
flow:RAM_pm_CtrlFlow (out -> in)

View file

@ -0,0 +1,42 @@
# Copy the left hand side
out_state:RAM_pm_CtrlPortState {
# Only set the output port to inactive if all connected input ports are set to active
RAM_active = ```
set_to_active = False
output_port = matched("out")
outgoing_flows = get_outgoing(output_port, "pm_CtrlFlow")
# for each flow: pm_CtrlFlow -> pm_CtrlIn <- pm_Of <- pm_CtrlPortState == state
all_input_port_states = [get_source(get_incoming(get_target(flow), "pm_Of")[0]) for flow in outgoing_flows]
input_port_state = matched("in_state")
for state in all_input_port_states:
is_active = get_slot_value(state, "active")
# If the state is not active and it is not the input port state we have matched and planned to set active
# Then we can't yet set this output port state to active
if not is_active and state != input_port_state:
set_to_active = True
break
# Set the attribute to the assigned value
set_to_active
```;
}
out:RAM_pm_CtrlOut
state_to_out:RAM_pm_Of (out_state -> out)
in_state:RAM_pm_CtrlPortState {
# Set the input port active
RAM_active = `True`;
}
in:RAM_pm_CtrlIn
state_to_in:RAM_pm_Of (in_state -> in)
flow:RAM_pm_CtrlFlow (out -> in)