diff --git a/optis/backend/__pycache__/agent.cpython-311.pyc b/optis/backend/__pycache__/agent.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..60b04fbb5f7da1a9b02b7cb1c95d6165a64c8e19
Binary files /dev/null and b/optis/backend/__pycache__/agent.cpython-311.pyc differ
diff --git a/optis/backend/__pycache__/environment.cpython-311.pyc b/optis/backend/__pycache__/environment.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e5dc19df2ec923f892d4833c9d5a8517fa0ebbc7
Binary files /dev/null and b/optis/backend/__pycache__/environment.cpython-311.pyc differ
diff --git a/optis/backend/__pycache__/eventlog.cpython-311.pyc b/optis/backend/__pycache__/eventlog.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d96682b9202d2f07c88f3b7ea777fc80cf74aabc
Binary files /dev/null and b/optis/backend/__pycache__/eventlog.cpython-311.pyc differ
diff --git a/optis/backend/__pycache__/simplesimmodel.cpython-311.pyc b/optis/backend/__pycache__/simplesimmodel.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2ae869e5646e548e0a24351de27367837deba250
Binary files /dev/null and b/optis/backend/__pycache__/simplesimmodel.cpython-311.pyc differ
diff --git a/optis/backend/agent.py b/optis/backend/agent.py
new file mode 100644
index 0000000000000000000000000000000000000000..36e347a00de4112decd9c189da2a9d6ea5028e2c
--- /dev/null
+++ b/optis/backend/agent.py
@@ -0,0 +1,72 @@
+import gymnasium as gym
+import numpy as np
+import environment
+from gymnasium.wrappers import FlattenObservation
+
+"""
+RL agent
+"""
+
+def q_learning(space, activities):
+ # Define the business process environment
+ env = environment.BusinessProcessEnv(space, activities)
+
+ # Define the Q-table
+ num_states = pow(2,14)
+
+ num_actions = env.action_space.n
+
+ Q = np.zeros((num_states, num_actions), dtype = int)
+
+ # Set the hyperparameters
+ alpha = 0.1 # learning rate
+ gamma = 0.1 # discount factor
+ epsilon = 0.1 # exploration rate
+
+ mean_time = 0
+ mean_reward = 0
+
+ # Train the agent using Q-learning
+ num_episodes = 1000
+ for episode in range(num_episodes):
+ state, _ = env.reset()
+ state = env.flatten_observation_to_int(state)
+ done = False
+ start = env.process.env.now
+ while not done:
+ # Choose an action based on the epsilon-greedy policy
+ if np.random.uniform(0, 1) < epsilon:
+ action = env.action_space.sample()
+ else:
+ action = np.argmax(Q[state])
+
+
+ # Execute the action and observe the next state and reward
+ next_state, reward, done, _ = env.step(action)
+
+ # Update the Q-value for the current state-action pair
+ Q[state][action] = (1-alpha)*Q[state][action] + alpha * (reward + gamma * np.max(Q[next_state]) - Q[state][action])
+ #Q[state][action] = (1-alpha)*Q[state][action] + alpha*reward
+
+ # Transition to the next state
+ state = next_state
+
+ time = env.process.env.now - start
+ mean_time += time
+ mean_reward += reward
+
+
+ if (episode % 20 == 19):
+ mean_reward /= 20
+ mean_time /= 20
+ print(f"Episode {episode-19} to episode {episode}: mean time = {mean_time}, mean reward: {mean_reward}")
+
+ if episode == 19:
+ start_reward = mean_reward
+
+ if episode == 999:
+ end_reward = mean_reward
+ improvement = end_reward - start_reward
+ print(f"Reward improved by {improvement}")
+
+ return Q
diff --git a/optis/backend/environment.py b/optis/backend/environment.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a7506b41753960fabec0f980a29ffc278c5fb98
--- /dev/null
+++ b/optis/backend/environment.py
@@ -0,0 +1,152 @@
+import gymnasium as gym
+import numpy as np
+import simpy
+import simplesimmodel as model
+
+"""
+Environment for the RL agent
+"""
+
+class BusinessProcessEnv(gym.Env):
+
+ def __init__(self, space, activities):
+ self.ressources = space[0]
+ self.case = space[1]
+ self.activities = activities
+
+ self.observation_space = gym.spaces.Dict(
+ {
+ 'process': gym.spaces.MultiDiscrete(self.ressources),
+ 'case': gym.spaces.MultiDiscrete(self.case),
+ 'event': gym.spaces.Discrete(self.activities)
+ }
+ )
+
+ self.action_space = gym.spaces.Discrete(self.activities)
+
+ self.current_state = {
+ 'process': np.array(self.ressources),
+ 'case': np.zeros(len(self.case), dtype=int),
+ 'event': 0
+ }
+
+ self.model_env = simpy.Environment()
+ self.process = model.BusinessProcess(self.model_env, self.ressources)
+ self.model_env.process(model.run_process(self.model_env, self.process))
+
+ self.reward = 0
+
+
+ def get_current_state(self, caseid):
+ process, case, event = model.get_current_state(self.process, caseid)
+ state = {
+ 'process': process,
+ 'case': case,
+ 'event': event
+ }
+ return state
+
+ def step(self, action):
+
+ self.process.next = action
+
+ if self.process.case_id in self.process.done_cases:
+ self.process.case_id = np.random.choice(self.process.active_cases)
+
+ case_obj = self.process.case_objects[self.process.case_id]
+ self.current_state = self.get_current_state(case_obj)
+
+ # print(f"{self.current_state['event']}")
+
+ start = self.process.env.now
+
+ self.process.flag = True
+
+ if self.process.is_valid(self.current_state['event'], action, case_obj):
+
+ while(self.process.flag):
+ self.model_env.step()
+
+ stop = self.process.env.now
+
+ case_obj = self.process.case_objects[self.process.case_id]
+
+ # print(f"Agent did case {self.process.case_id} activity {action}.")
+
+ next_state = self.get_current_state(case_obj)
+ self.current_state = next_state
+ next_state = self.flatten_observation_to_int(next_state)
+
+ time = stop - start
+ reward = 10000 - time
+ self.reward += reward
+ done = True if (len(self.process.done_cases) == 5 or len(self.process.active_cases) == 0) else False
+ return next_state, self.reward, done, None
+
+ else:
+ self.reward += 0
+ next_state = self.flatten_observation_to_int(self.current_state)
+ done = False
+ return next_state, self.reward, done, None
+
+
+ def reset(self, seed=None, options=None):
+ # Reset the environment to the initial state
+ # Implement a function which extracts the current state from an event log / simulation model
+ super().reset(seed=seed)
+
+ self.current_state = {
+ 'process': np.array(self.ressources),
+ 'case': np.zeros(len(self.case), dtype=int),
+ 'event': 0
+ }
+ self.current_step = 0
+
+ self.model_env = simpy.Environment()
+ self.process = model.BusinessProcess(self.model_env, self.ressources)
+ self.model_env.process(model.run_process(self.model_env, self.process))
+ self.process.done_cases = set([])
+
+ self.reward = 0
+
+ return self.current_state, None
+
+
+ def render(self, mode='human'):
+ # Render the current state of the environment
+ pass
+
+
+ def flatten_observation(self, observation):
+ flattened = []
+ for i in observation['process']: flattened.append(i)
+ for j in observation['case']: flattened.append(j)
+ flattened.append(observation['event'])
+
+ return flattened
+
+ def flatten_observation_to_int(self, observation):
+ state = 0
+ state += observation['event']*pow(2,10)
+ state += observation['case'][1]*pow(2,2)
+ state += observation['case'][2]*pow(2,2)
+ event = observation['event']
+ if event == 0:
+ state += observation['process'][0]*pow(2,6)
+ elif event == 1:
+ state += observation['process'][1]*pow(2,6)
+ elif 1 < event <=3:
+ state += observation['process'][2]*pow(2,6)+observation['process'][3]*pow(2,7)+observation['process'][4]*pow(2,8)
+ elif 3 < event <=6:
+ state += observation['process'][5]*pow(2,6)+observation['process'][6]*pow(2,7)
+ elif 6 < event <= 8:
+ state += observation['process'][7]*pow(2,6)+observation['process'][8]*pow(2,7)+observation['process'][9]*pow(2,8)
+ elif 8 < event <= 11:
+ state += observation['process'][10]*pow(2,6)+observation['process'][11]*pow(2,7)+observation['process'][12]*pow(2,8)
+ elif 11 < event <= 14:
+ state += observation['process'][0]*pow(2,6)
+ else:
+ pass
+
+ return state
+
diff --git a/optis/backend/eventlog.py b/optis/backend/eventlog.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a3625ef7aca88f1debea601923e4acfb86ad3a5
--- /dev/null
+++ b/optis/backend/eventlog.py
@@ -0,0 +1,172 @@
+import pandas as pd
+import simplesimmodel as model
+import numpy as np
+
+"""
+Event log generator for our simulation model:
+- generate an event log
+- update an event log (adding new events)
+- export event log
+- get current state of an event log
+"""
+
+def add_start_event(process, event_id, case_id, activity, start_timestamp):
+ process.event_log.append(event_id)
+ process.event_log[event_id] = {
+ 'CaseID': case_id,
+ 'Activity': activity,
+ 'StartTimestamp': float(start_timestamp),
+ 'EndTimestamp': None
+ }
+ process.event_counter += 1
+
+def add_end_event(process, event_id, end_timestamp):
+ event = process.event_log[event_id]
+ event['EndTimestamp'] = end_timestamp
+
+
+def export_to_csv(process, file_path):
+ event_log_df = pd.DataFrame.from_dict(process.event_log)
+ event_log_df.to_csv(file_path, index=False)
+
+def export_to_xes(process, file_path):
+ # Use appropriate code to export to XES format
+ pass
+
+def get_active_cases():
+ event_log = pd.read_csv(r'D:\test\optis.csv')
+ active_cases = event_log.groupby('CaseID').filter(lambda x: 'order completed' not in x['Activity'].values)['CaseID'].unique().tolist()
+ return active_cases
+
+
+def get_state(case_id):
+
+ process = []
+ num_s = 1
+ process.append(num_s)
+ num_ot = 5
+ process.append(num_ot)
+ num_sh_a = 3
+ process.append(num_sh_a)
+ num_sh_b = 3
+ process.append(num_sh_b)
+ num_sh_c = 3
+ process.append(num_sh_c)
+ num_m_a = 3
+ process.append(num_m_a)
+ num_m_b = 2
+ process.append(num_m_b)
+ num_p_a = 4
+ process.append(num_p_a)
+ num_p_b = 5
+ process.append(num_p_b)
+ num_p_c = 4
+ process.append(num_p_c)
+ num_ds_a = 8
+ process.append(num_ds_a)
+ num_ds_b = 8
+ process.append(num_ds_b)
+ num_ds_c = 8
+ process.append(num_ds_c)
+
+ case = []
+ for i in range(15):
+ case.append(0)
+
+ activity_mapping = {
+ 'place order': 1,
+ 'arrange standard order': 2,
+ 'arrange custom order': 3,
+ 'pick from stock A': 4,
+ 'pick from stock B': 5,
+ 'pick from stock C': 6,
+ 'manufacture A': 7,
+ 'manufacture B': 8,
+ 'pack A': 9,
+ 'pack B': 10,
+ 'pack C': 11,
+ 'attempt delivery A': 12,
+ 'attempt delivery B': 13,
+ 'attempt delivery C': 14,
+ 'order completed': 15,
+ }
+
+ event_log = pd.read_csv(r'D:\test\optis.csv')
+ # Sort the event log by case ID and start timestamp
+ event_log.sort_values(by=['CaseID', 'StartTimestamp'], inplace=True)
+
+ # Group the event log by case ID and get the last activity for each case
+ last_activities = event_log.groupby('CaseID').tail(1).reset_index()
+
+ # Remap the activity names to numbers using the mapping dictionary
+ last_activities['Activity'] = last_activities['Activity'].map(activity_mapping)
+
+ # Filter the cases where the end timestamp of the last activity is None or empty
+ unfinished_cases = last_activities[last_activities['EndTimestamp'].isnull()]['CaseID'].tolist()
+
+ # Update the state of the ressources given all unfinished cases
+ for i in unfinished_cases:
+ activity = last_activities[last_activities['CaseID'] == i]['Activity'].values[0]
+ if activity == 1 or activity == 15:
+ process[0] -= 1
+ elif activity == 2 or activity == 3:
+ process[1] -= 1
+ else:
+ process[activity-2] -= 1
+
+ # Get the state of the case for the given Case ID
+ filtered_log = event_log[event_log['CaseID'] == case_id]
+ activities = filtered_log['Activity'].map(activity_mapping).tolist()
+ for i in activities:
+ case[i-1] += 1
+
+ # Get the last event for the given Case ID
+ event = last_activities[last_activities['CaseID'] == case_id]['Activity'].values[0]
+
+ state = {
+ 'process': process,
+ 'case': case,
+ 'event': event
+ }
+
+ print(state)
+
+ """
+ flattened = []
+ for i in state['process']: flattened.append(i)
+ for j in state['case']: flattened.append(j)
+ flattened.append(state['event'])
+
+
+ flattened = 0
+ flattened += state['event']
+ for i in state['case']: flattened += i
+ for j in state['process']: flattened += j*process[j]
+
+ print(flattened)
+ """
+ flat_state = 0
+ flat_state += state['event']*pow(2,10)
+ print(flat_state)
+ flat_state += state['case'][1]*pow(2,1)
+ flat_state += state['case'][2]*pow(2,2)
+ event = state['event']
+ if event == 0:
+ flat_state += state['process'][0]*pow(2,6)
+ elif event == 1:
+ flat_state += state['process'][1]*pow(2,6)
+ elif 1 < event <=3:
+ flat_state += state['process'][2]*pow(2,6)+state['process'][3]*pow(2,7)+state['process'][4]*pow(2,8)
+ elif 3 < event <=6:
+ flat_state += state['process'][5]*pow(2,6)+state['process'][6]*pow(2,7)
+ elif 6 < event <= 8:
+ flat_state += state['process'][7]*pow(2,6)+state['process'][8]*pow(2,7)+state['process'][9]*pow(2,8)
+ elif 8 < event <= 11:
+ flat_state += state['process'][10]*pow(2,6)+state['process'][11]*pow(2,7)+state['process'][12]*pow(2,8)
+ elif 11 < event <= 14:
+ flat_state += state['process'][0]*pow(2,6)
+ else:
+ pass
+
+ print(flat_state)
+ return flat_state
diff --git a/optis/backend/main.py b/optis/backend/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..fdee31d6eb3e8d2bf65037450ec61528c9b21529
--- /dev/null
+++ b/optis/backend/main.py
@@ -0,0 +1,91 @@
+import simpy
+import random
+import numpy as np
+import simplesimmodel as model
+import environment
+import agent
+import eventlog as log
+import pandas as pd
+
+def main():
+ # Setup
+ # we can use a random seed if we want to generate the same results every time (maybe useful later for the training)
+ # random.seed(42)
+ # initialize the number of resources
+
+ process = []
+ num_s = 1
+ process.append(num_s)
+ num_ot = 5
+ process.append(num_ot)
+ num_sh_a = 3
+ process.append(num_sh_a)
+ num_sh_b = 3
+ process.append(num_sh_b)
+ num_sh_c = 3
+ process.append(num_sh_c)
+ num_m_a = 3
+ process.append(num_m_a)
+ num_m_b = 2
+ process.append(num_m_b)
+ num_p_a = 4
+ process.append(num_p_a)
+ num_p_b = 5
+ process.append(num_p_b)
+ num_p_c = 4
+ process.append(num_p_c)
+ num_ds_a = 7
+ process.append(num_ds_a)
+ num_ds_b = 7
+ process.append(num_ds_b)
+ num_ds_c = 7
+ process.append(num_ds_c)
+
+ case = []
+ for i in range(15):
+ case.append(1)
+
+ space = [process, case]
+ activities = 16
+
+ # q learning
+ Q = agent.q_learning(space, activities)
+ # print(Q)
+
+ # generate event log
+ env = simpy.Environment()
+ business_process = model.BusinessProcess(env, process)
+ business_process.event_log_flag = True
+ env.process(model.run_process(env, business_process))
+ env.run(until = 10000)
+ log.export_to_csv(business_process, r'D:\test\optis.csv')
+
+ # extract active cases from event log
+ active_cases = log.get_active_cases()
+ print(active_cases)
+
+ # test agent
+ for i in range(20):
+ caseid = random.choice(active_cases)
+ state = log.get_state(caseid)
+
+ action = np.argmax(Q[state])
+ print(action)
+ #print(Q)
+ print(Q[state])
+
+ state = Q[0]
+ action = np.argmax(state)
+ print(action)
+ print(state)
+
+ state = Q[64]
+ action = np.argmax(state)
+ print(action)
+ print(state)
+
+
+
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/optis/backend/simplesimmodel.py b/optis/backend/simplesimmodel.py
new file mode 100644
index 0000000000000000000000000000000000000000..18cb95273cd687ac419a8d7c8cf3fce44011e738
--- /dev/null
+++ b/optis/backend/simplesimmodel.py
@@ -0,0 +1,553 @@
+import simpy
+import random
+import numpy as np
+import environment
+import agent
+import eventlog as log
+
+"""
+Simulation model for a simpler business process, including:
+- a class representing the process
+- a function representing the process flow
+- a function to run the process
+"""
+
+
+"""
+This class represents the process with its:
+- ressources: system, managers, stock handlers (a, b and c), manufacturers (a and b), delivery services (a, b and c)
+- activities
+"""
+class BusinessProcess(object):
+ """ Initialize the process with the number of the different types of ressources """
+ def __init__(self, env, ressources):
+
+ # initialize simulation environment
+ self.env = env
+
+ # initialize ressources
+ self.system = simpy.Resource(env, ressources[0])
+ self.order_taker = simpy.Resource(env, ressources[1])
+ self.stock_handler_a = simpy.Resource(env, ressources[2])
+ self.stock_handler_b = simpy.Resource(env, ressources[3])
+ self.stock_handler_c = simpy.Resource(env, ressources[4])
+ self.manufacturer_a = simpy.Resource(env, ressources[5])
+ self.manufacturer_b = simpy.Resource(env, ressources[6])
+ self.packer_a = simpy.Resource(env, ressources[7])
+ self.packer_b = simpy.Resource(env, ressources[8])
+ self.packer_c = simpy.Resource(env, ressources[9])
+ # capacity of each delivery service instead of numbers of workers
+ self.delivery_service_a = simpy.Resource(env, ressources[10])
+ self.delivery_service_b = simpy.Resource(env, ressources[11])
+ self.delivery_service_c = simpy.Resource(env, ressources[12])
+
+ # initialize lists with active cases and all case objects
+ self.active_cases = [0, 1, 2]
+ case_0 = Case(0)
+ case_1 = Case(1)
+ case_2 = Case(2)
+ self.case_objects = [case_0, case_1, case_2]
+
+ # flag indicates whether the process is currently controlled by the agent and we set it on false every time the agent does an activity
+ self.flag = True
+
+ self.next = 0
+
+ self.case_id = 0
+
+ self.done_cases = set([])
+
+ self.event_log_flag = False
+ self.event_log = []
+ self.event_counter = 0
+
+ # random.seed(1)
+
+ def place_order(self, case):
+ yield self.env.timeout(0)
+
+ def arrange_standard_order(self, case):
+ yield self.env.timeout(random.randint(10, 15))
+
+ def arrange_custom_order(self, case):
+ yield self.env.timeout(random.randint(20, 30))
+
+ def pick_from_stock_a(self, case):
+ yield self.env.timeout(random.randint(10, 40))
+
+ def pick_from_stock_b(self, case):
+ yield self.env.timeout(random.randint(20, 60))
+
+ def pick_from_stock_c(self, case):
+ yield self.env.timeout(random.randint(30, 80))
+
+ def manufacture_a(self, case):
+ yield self.env.timeout(random.randint(240, 360))
+
+ def manufacture_b(self, case):
+ yield self.env.timeout(random.randint(360, 480))
+
+ def pack_a(self, case):
+ yield self.env.timeout(random.randint(10, 20))
+
+ def pack_b(self, case):
+ yield self.env.timeout(random.randint(15, 30))
+
+ def pack_c(self, case):
+ yield self.env.timeout(random.randint(25, 50))
+
+ def attempt_delivery_a(self, case):
+ yield self.env.timeout(random.randint(720, 1440))
+
+ def attempt_delivery_b(self, case):
+ yield self.env.timeout(random.randint(1440, 2160))
+
+ def attempt_delivery_c(self, case):
+ yield self.env.timeout(random.randint(1440, 2880))
+
+ def order_completed(self, case):
+ yield self.env.timeout(1)
+
+ def is_valid(self, event, action, case_obj):
+ if event == 0 and action == 1:
+ return True
+ elif event == 1 and (action == 2 or action == 3):
+ return True
+ elif event == 2 and case_obj.standard_order and (action == 4 or action == 5 or action == 6):
+ return True
+ elif event == 3 and (not case_obj.standard_order) and (action == 7 or action == 8):
+ return True
+ elif (4 <= event <= 8) and (action == 9 or action == 10 or action == 11):
+ return True
+ elif (9 <= event <= 11) and (12 <= action <=14):
+ return True
+ elif (12 <= event <=14) and action == 15:
+ return True
+ else:
+ return False
+
+"""
+A class representing a single case from the process. Every case has an (unique) case id,
+state showing which activities have been already done, current activity being executed,
+agent flag showing if the agent is currently controlling the case and
+a flag whether the order is standard
+"""
+class Case(object):
+ def __init__(self, case):
+ self.case_id = case
+ self.state = np.zeros(15, dtype = int)
+ self.current = 0
+ self.agent = False
+ self.standard_order = True
+
+"""
+A function, which represents the process model with it's control flow and ressource allocation.
+A customer places an order at the system and then is redirected to the order takers, where it's arranged
+what type of order the customer wants - standard or custom. After that depending on whether the order
+is standard or custom, respectively a stock or manufacturer is chosen.
+Afterwards one of three packing options is chosen and executed.
+Following the packing one of the three delivery services deliver the order.
+With that the order is completed.
+"""
+def execute_case(env, case, process):
+ # create a case object to keep track of case attributes if not already existing
+ if len(process.case_objects) <= case:
+ case_obj = Case(case)
+
+ # add the case to the process's active cases list
+ process.case_objects.append(case_obj)
+ process.active_cases.append(case)
+
+ # if the case is one of the first three choose it from the list
+ if len(process.case_objects) <= 3:
+ case_obj = process.case_objects[case]
+
+ # if case chosen by agent set agent flag to true
+ if process.case_id == case:
+ case_obj.agent = True
+
+ # place order
+ case_obj.state[0] += 1
+ case_obj.current = 1
+ with process.system.request() as request:
+ yield request
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "place order", env.now)
+ yield env.process(process.place_order(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+ # if case_obj.agent: print(f"Case {case}: 'placed order' at {env.now:.2f}")
+
+
+ # if the last action was made from the agent set the process flag to be able to return to the environment's step function
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+
+ # before a new action is executed check if the agent is controlling the case and set the flag to true if yes
+ if process.case_id == case: case_obj.agent = True
+
+ # standard order XOR custom order
+ choice = random.randint(2,3) if not case_obj.agent else process.next
+ if choice == 2:
+ case_obj.standard_order = True
+ case_obj.state[1] += 1
+ case_obj.current = 2
+ with process.order_taker.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'arrange standard order' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "arrange standard order", env.now)
+ yield env.process(process.arrange_standard_order(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+ # if case_obj.agent: print(f"Case {case}: finished 'arrange standard order' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+ else:
+ case_obj.state[2] += 1
+ case_obj.current = 3
+ case_obj.standard_order = False
+ with process.order_taker.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'arrange custom order' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "arrange custom order", env.now)
+ yield env.process(process.arrange_custom_order(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+ # if case_obj.agent: print(f"Case {case}: finished 'arrange custom order' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+
+
+ if process.case_id == case: case_obj.agent = True
+
+ if case_obj.standard_order and (not case_obj.agent):
+ choice = random.randint(4,6)
+ elif (not case_obj.standard_order) and (not case_obj.agent):
+ choice = random.randint(7,8)
+ else:
+ choice = process.next
+
+ # choose stock or manufacturer
+ if choice == 4:
+ case_obj.state[3] += 1
+ case_obj.current = 4
+ with process.stock_handler_a.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'pick from stock A' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "pick from stock A", env.now)
+ yield env.process(process.pick_from_stock_a(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+ # if case_obj.agent: print(f"Case {case}: finished 'pick from stock A' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+ elif choice == 5:
+ case_obj.state[4] += 1
+ case_obj.current = 5
+ with process.stock_handler_b.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'pick from stock B' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "pick from stock B", env.now)
+ yield env.process(process.pick_from_stock_b(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+
+ # if case_obj.agent: print(f"Case {case}: finished 'pick from stock B' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+ elif choice == 6:
+ case_obj.state[5] += 1
+ case_obj.current = 6
+ with process.stock_handler_c.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'pick from stock C' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "pick from stock C", env.now)
+ yield env.process(process.pick_from_stock_c(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+ # if case_obj.agent: print(f"Case {case}: finished 'pick from stock C' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+ elif choice == 7:
+ case_obj.state[6] += 1
+ case_obj.current = 7
+ with process.manufacturer_a.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'manufacture A' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "manufacture A", env.now)
+ yield env.process(process.manufacture_a(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+
+ # if case_obj.agent: print(f"Case {case}: finished 'manufacture A' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+ else:
+ case_obj.state[7] += 1
+ case_obj.current = 8
+ with process.manufacturer_b.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'manufacture B' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "manufacture B", env.now)
+ yield env.process(process.manufacture_b(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+ # if case_obj.agent: print(f"Case {case}: finished 'manufacture B' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+
+
+ if process.case_id == case: case_obj.agent = True
+
+ choice = random.randint(9,11) if not case_obj.agent else process.next
+
+ if choice == 9:
+ case_obj.state[8] += 1
+ case_obj.current = 9
+ with process.packer_a.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'pack A' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "pack A", env.now)
+ yield env.process(process.pack_a(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+
+ # if case_obj.agent: print(f"Case {case}: finished 'pack A' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+ elif choice == 10:
+ case_obj.state[9] += 1
+ case_obj.current = 10
+ with process.packer_b.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'pack B' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "pack B", env.now)
+ yield env.process(process.pack_b(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+
+ # if case_obj.agent: print(f"Case {case}: finished 'pack B' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+
+ with process.system.request() as request:
+ yield request
+ else:
+ case_obj.state[10] += 1
+ case_obj.current = 11
+ with process.packer_c.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'pack C' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "pack C", env.now)
+ yield env.process(process.pack_c(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+
+ # if case_obj.agent: print(f"Case {case}: finished 'pack C' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+
+ if process.case_id == case: case_obj.agent = True
+
+ # choose delivery
+ choice = random.randint(12,14) if not case_obj.agent else process.next
+ if choice == 12:
+ case_obj.state[11] += 1
+ case_obj.current = 12
+ with process.delivery_service_a.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'attempt delivery A' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "attempt delivery A", env.now)
+ yield env.process(process.attempt_delivery_a(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+
+ # if case_obj.agent: print(f"Case {case}: finished 'attempt delivery A' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+ elif choice == 13:
+ case_obj.state[12] += 1
+ case_obj.current = 13
+ with process.delivery_service_b.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'attempt delivery B' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "attempt delivery B", env.now)
+ yield env.process(process.attempt_delivery_b(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+
+ # if case_obj.agent: print(f"Case {case}: finished 'attempt delivery B' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+ else:
+ case_obj.state[13] += 1
+ case_obj.current = 14
+ with process.delivery_service_c.request() as request:
+ yield request
+ # if case_obj.agent: print(f"Case {case}: started 'attempt delivery C' at {env.now:.2f}")
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "attempt delivery C", env.now)
+ yield env.process(process.attempt_delivery_c(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+
+ # if case_obj.agent: print(f"Case {case}: finished 'attempt delivery C' at {env.now:.2f}")
+ if case_obj.agent:
+ process.flag = False
+ with process.system.request() as request:
+ yield request
+
+
+ if process.case_id == case: case_obj.agent = True
+
+ # case completed
+ case_obj.state[14] += 1
+ case_obj.current = 15
+ with process.system.request() as request:
+ yield request
+ if process.event_log_flag:
+ event_counter = process.event_counter
+ log.add_start_event(process, event_counter, case, "order completed", env.now)
+ yield env.process(process.order_completed(case))
+ if process.event_log_flag:
+ log.add_end_event(process, event_counter, env.now)
+ # if case_obj.agent: print(f"Case {case}: 'completed' at {env.now:.2f}")
+
+
+ if case in process.active_cases:
+ process.active_cases.remove(case)
+
+ if case_obj.agent:
+ for i in process.case_objects:
+ if (i.current == 15) and (i.case_id in process.active_cases) and (i.case_id != case):
+ process.active_cases.remove(i.case_id)
+ process.done_cases.add(process.case_id)
+ process.flag = False
+
+
+"""
+Get the cureent state of the process and a specific case
+- available ressouces
+- events which already happened in the case
+- current event of the case
+"""
+def get_current_state(process, case):
+ process_state = []
+
+ num_system = process.system.capacity - process.system.count
+ process_state.append(num_system)
+
+ num_order_taker = process.order_taker.capacity - process.order_taker.count
+ process_state.append(num_order_taker)
+
+ num_stock_handler_a = process.stock_handler_a.capacity - process.stock_handler_a.count
+ process_state.append(num_stock_handler_a)
+
+ num_stock_handler_b = process.stock_handler_b.capacity - process.stock_handler_b.count
+ process_state.append(num_stock_handler_b)
+
+ num_stock_handler_c = process.stock_handler_c.capacity - process.stock_handler_c.count
+ process_state.append(num_stock_handler_c)
+
+ num_manufacturer_a = process.manufacturer_a.capacity - process.manufacturer_a.count
+ process_state.append(num_manufacturer_a)
+
+ num_manufacturer_b = process.manufacturer_b.capacity - process.manufacturer_b.count
+ process_state.append(num_manufacturer_b)
+
+ num_packer_a = process.packer_a.capacity - process.packer_a.count
+ process_state.append(num_packer_a)
+
+ num_packer_b = process.packer_b.capacity - process.packer_b.count
+ process_state.append(num_packer_b)
+
+ num_packer_c = process.packer_c.capacity - process.packer_c.count
+ process_state.append(num_packer_c)
+
+ num_delivery_service_a = process.delivery_service_a.capacity - process.delivery_service_a.count
+ process_state.append(num_delivery_service_a)
+
+ num_delivery_service_b = process.delivery_service_b.capacity - process.delivery_service_b.count
+ process_state.append(num_delivery_service_b)
+
+ num_delivery_service_c = process.delivery_service_c.capacity - process.delivery_service_c.count
+ process_state.append(num_delivery_service_c)
+
+ cur_case = case.state
+
+ event = case.current
+
+ return process_state, cur_case, event
+
+"""
+Defines how often new orders (cases) come in and starts executing them
+"""
+def run_process(env, process):
+ # process = Process(env, num_ot, num_m, num_sh_a, num_sh_b, num_sh_c, num_m_a, num_m_b, num_ds_a, num_ds_b, num_ds_c)
+
+ # the waiting orders
+ for case in range(3):
+ env.process(execute_case(env, case, process))
+
+ # the new incoming orders
+ while case < 1000:
+ waittime = random.randint(10,15)
+ if case % 20 == 0:
+ waittime = 100
+ yield env.timeout(waittime) # Wait a bit before generating a new case
+
+ case += 1
+ # process.active_cases.append(case)
+ env.process(execute_case(env, case, process))
+