diff --git a/Simpy_Tutorial/environment.py b/Simpy_Tutorial/environment.py index 78147453a4bef38af57b870656b09ffdc3885bd8..b94a844174eb4e5821b6664c55e8c6f26c5ef20d 100644 --- a/Simpy_Tutorial/environment.py +++ b/Simpy_Tutorial/environment.py @@ -2,6 +2,7 @@ import gymnasium as gym import numpy as np import processmodel as model import simpy +import eventlog """ Environment for the RL agent @@ -40,6 +41,8 @@ class BusinessProcessEnv(gym.Env): self.reward = 0 + self.bigeventlog = [] + def get_current_state(self, caseid): process, case, event = model.get_current_state(self.process, caseid) @@ -94,6 +97,7 @@ class BusinessProcessEnv(gym.Env): 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) diff --git a/Simpy_Tutorial/eventlog.py b/Simpy_Tutorial/eventlog.py index 27e541abb4b1feccb8a93c035a884f9ab9816c7d..8a31eaf99ca99fbcee7db494ea8598148373147e 100644 --- a/Simpy_Tutorial/eventlog.py +++ b/Simpy_Tutorial/eventlog.py @@ -11,21 +11,28 @@ def add_start_event(process, event_id, case_id, activity, start_timestamp): process.event_log[event_id] = { 'CaseID': case_id, 'Activity': activity, - 'StartTimestamp': start_timestamp, + 'StartTimestamp': float(start_timestamp), + 'EndTimestamp': None } def add_end_event(process, event_id, end_timestamp): - if event_id in process.event_log: - event = process.event_log[event_id] - event['EndTimestamp'] = end_timestamp - process.event_log.append(event) - del process.event_log[event_id] + # if event_id in process.event_log: + event = process.event_log[event_id] + event['EndTimestamp'] = end_timestamp + # process.event_log.append(event) + # del process.event_log[event_id] # add functions for adding events with their attributes to the log +<<<<<<<< HEAD:simpy_tutorial/eventlog.py +def export_to_csv(env, file_path): + event_log_df = pd.DataFrame.from_dict(env.bigeventlog) + event_log_df.to_csv(file_path) +======== 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) +>>>>>>>> 575b59827b5f928da4165070a4a1d51a85eed774:Simpy_Tutorial/eventlog.py def export_to_xes(process, file_path): # Use appropriate code to export to XES format diff --git a/Simpy_Tutorial/processmodel.py b/Simpy_Tutorial/processmodel.py index ea2fae666eee0a941d7ddad1394cb39ebbbe9b9f..1f158e087ba8c572f4b48a5f84c5634609e0a24a 100644 --- a/Simpy_Tutorial/processmodel.py +++ b/Simpy_Tutorial/processmodel.py @@ -78,12 +78,13 @@ def execute_case(env, case, process): case_obj.state[0] += 1 case_obj.current = 1 process.event_log.append(process.event_counter) - eventlog.add_start_event(process, process.event_counter, case, "a", env.now) event_counter = process.event_counter + eventlog.add_start_event(process, event_counter, case, "a", env.now) process.event_counter += 1 print(f"Case {case}: started activity a at {env.now:.2f}") yield env.process(process.a(case)) - eventlog.add_end_event(process, event_counter, env.now) + end = int(env.now) + eventlog.add_end_event(process, event_counter, end) print(f"Case {case}: finished activity a at {env.now:.2f}") if case_obj.agent: process.flag = False @@ -250,8 +251,15 @@ def main(): print(state['event']) print(business_env.flatten_observation(state)) rl.q_learning(space, activities) +<<<<<<<< HEAD:Simpy_Tutorial/processmodel.py path = r"C:\Users\nourm\OneDrive\Desktop\Nour\optis_app\out.csv" eventlog.export_to_csv(business_env.process, path) +======== + + el = business_env.bigeventlog + + # eventlog.export_to_csv(business_env, r'D:\test\optis.csv') +>>>>>>>> origin/aleks2:simpy_tutorial/processmodel.py diff --git a/Simpy_Tutorial/rlalgorithm.py b/Simpy_Tutorial/rlalgorithm.py index ac44e92e4a1ebc892cfcf8bd610fb5a5d2db550a..ed3981a8307566359e6e4d46dbdbbdd9a4a60e44 100644 --- a/Simpy_Tutorial/rlalgorithm.py +++ b/Simpy_Tutorial/rlalgorithm.py @@ -3,6 +3,7 @@ import numpy as np import environment import processmodel as model from gymnasium.wrappers import FlattenObservation +import eventlog def q_learning(space, activities): # Define the business process environment @@ -29,7 +30,7 @@ def q_learning(space, activities): epsilon = 0.1 # exploration rate # Train the agent using Q-learning - num_episodes = 10 + num_episodes = 2 for episode in range(num_episodes): state, _ = env.reset() state = env.flatten_observation(state) @@ -52,4 +53,7 @@ def q_learning(space, activities): # Transition to the next state state = next_state + env.bigeventlog += env.process.event_log + eventlog.export_to_csv(env, r'D:\test\optis.csv') + print(f"Episode {episode} ended.") \ No newline at end of file diff --git a/__pycache__/environment.cpython-311.pyc b/__pycache__/environment.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4a8542792b100289cbe8d758a23e2a2279a6cc1 Binary files /dev/null and b/__pycache__/environment.cpython-311.pyc differ diff --git a/__pycache__/eventlog.cpython-311.pyc b/__pycache__/eventlog.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..726340a5c7d89836ad99cf2cc6f04f840138e0d4 Binary files /dev/null and b/__pycache__/eventlog.cpython-311.pyc differ diff --git a/__pycache__/processmodel.cpython-311.pyc b/__pycache__/processmodel.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6285a50268e9a8c80f8bd8a91f005bfc3d523237 Binary files /dev/null and b/__pycache__/processmodel.cpython-311.pyc differ diff --git a/__pycache__/rlalgorithm.cpython-311.pyc b/__pycache__/rlalgorithm.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23d7225e816b44e1fc9cfb2a8718f19fcf3f889b Binary files /dev/null and b/__pycache__/rlalgorithm.cpython-311.pyc differ diff --git a/backend/__pycache__/agent.cpython-311.pyc b/backend/__pycache__/agent.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87f701a04a31d900d91fd18b6bc837516be90163 Binary files /dev/null and b/backend/__pycache__/agent.cpython-311.pyc differ diff --git a/backend/__pycache__/environment.cpython-311.pyc b/backend/__pycache__/environment.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51daa198751b4836531ffe9cc564ed462aed29b8 Binary files /dev/null and b/backend/__pycache__/environment.cpython-311.pyc differ diff --git a/backend/__pycache__/simplesimmodel.cpython-311.pyc b/backend/__pycache__/simplesimmodel.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f8f062a2173a3f3440055bc14475b66274869c3 Binary files /dev/null and b/backend/__pycache__/simplesimmodel.cpython-311.pyc differ diff --git a/backend/__pycache__/simulationmodel.cpython-311.pyc b/backend/__pycache__/simulationmodel.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..faf0672706b339b6fd1e7ee5c06392cb3993a759 Binary files /dev/null and b/backend/__pycache__/simulationmodel.cpython-311.pyc differ diff --git a/backend/agent.py b/backend/agent.py new file mode 100644 index 0000000000000000000000000000000000000000..111df067c5097be66021c5d64734dcc9f66f6117 --- /dev/null +++ b/backend/agent.py @@ -0,0 +1,69 @@ +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 = 1 + + process_space = env.observation_space['process'].nvec + case_space = env.observation_space['case'].nvec + event_space = env.observation_space['event'].n + + for i in process_space: num_states *= i + for i in case_space: num_states *= i + num_states *= event_space + + num_actions = env.action_space.n + + Q = np.zeros((num_states, num_actions), dtype=np.int16) + + # Set the hyperparameters + alpha = 0.1 # learning rate + gamma = 0.99 # discount factor + epsilon = 0.1 # exploration rate + + mean_time = 0 + + # Train the agent using Q-learning + num_episodes = 100 + for episode in range(num_episodes): + state, _ = env.reset() + state = env.flatten_observation(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] = Q[state][action] + alpha * (reward + gamma * np.max(Q[next_state]) - Q[state][action]) + + # Transition to the next state + state = next_state + + time = env.process.env.now - start + mean_time += time + + """ + if (episode % 20 == 19): + mean_time /= 20 + print(f"Episode {episode-19} to episode {episode}: mean time = {mean_time}") + """ + + print(f"Episode {episode}: time = {time}") \ No newline at end of file diff --git a/backend/environment.py b/backend/environment.py new file mode 100644 index 0000000000000000000000000000000000000000..b96b6425d7216e12269ab8b8a50577c25a72ae05 --- /dev/null +++ b/backend/environment.py @@ -0,0 +1,128 @@ +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.done_cases = set([]) + + 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(next_state) + + self.reward += -(stop - start) + 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 += -100 + next_state = self.flatten_observation(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 + + diff --git a/backend/eventlog.py b/backend/eventlog.py new file mode 100644 index 0000000000000000000000000000000000000000..12c771efaaad9a950ead2e04aeba2be9f8c4cbb0 --- /dev/null +++ b/backend/eventlog.py @@ -0,0 +1,39 @@ +import pandas as pd +import simplesimmodel as model + +""" +Event log generator for our simulation model: +- generate an event log +- update an event log (adding new events) +""" + +def add_start_event(process, event_id, case_id, activity, start_timestamp): + process.event_log[event_id] = { + 'CaseID': case_id, + 'Activity': activity, + 'StartTimestamp': float(start_timestamp), + 'EndTimestamp': None + } + +def add_end_event(process, event_id, end_timestamp): + # if event_id in process.event_log: + event = process.event_log[event_id] + event['EndTimestamp'] = end_timestamp + # process.event_log.append(event) + # del process.event_log[event_id] + +# add functions for adding events with their attributes to the log + +<<<<<<<< HEAD:Simpy_Tutorial/eventlog.py +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_csv(env, file_path): + event_log_df = pd.DataFrame.from_dict(env.bigeventlog) + event_log_df.to_csv(file_path) +>>>>>>>> origin/aleks2:backend/eventlog.py + +def export_to_xes(process, file_path): + # Use appropriate code to export to XES format + pass \ No newline at end of file diff --git a/backend/simplesimmodel.py b/backend/simplesimmodel.py new file mode 100644 index 0000000000000000000000000000000000000000..fe23eb4b077fbfcf15c62daa4a7e844ac1e875e0 --- /dev/null +++ b/backend/simplesimmodel.py @@ -0,0 +1,519 @@ +import simpy +import random +import numpy as np +import environment +import agent + +""" +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([]) + + 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 + with process.system.request() as request: + yield request + case_obj.state[0] += 1 + case_obj.current = 1 + yield env.process(process.place_order(case)) + 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 + with process.order_taker.request() as request: + yield request + case_obj.state[1] += 1 + case_obj.current = 2 + if case_obj.agent: print(f"Case {case}: started 'arrange standard order' at {env.now:.2f}") + yield env.process(process.arrange_standard_order(case)) + 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.standard_order = False + with process.order_taker.request() as request: + yield request + case_obj.state[2] += 1 + case_obj.current = 3 + if case_obj.agent: print(f"Case {case}: started 'arrange custom order' at {env.now:.2f}") + yield env.process(process.arrange_custom_order(case)) + 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: + with process.stock_handler_a.request() as request: + yield request + case_obj.state[3] += 1 + case_obj.current = 4 + if case_obj.agent: print(f"Case {case}: started 'pick from stock A' at {env.now:.2f}") + yield env.process(process.pick_from_stock_a(case)) + 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: + with process.stock_handler_b.request() as request: + yield request + case_obj.state[4] += 1 + case_obj.current = 5 + if case_obj.agent: print(f"Case {case}: started 'pick from stock B' at {env.now:.2f}") + yield env.process(process.pick_from_stock_b(case)) + 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: + with process.stock_handler_c.request() as request: + yield request + case_obj.state[5] += 1 + case_obj.current = 6 + if case_obj.agent: print(f"Case {case}: started 'pick from stock C' at {env.now:.2f}") + yield env.process(process.pick_from_stock_c(case)) + 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: + with process.manufacturer_a.request() as request: + yield request + case_obj.state[6] += 1 + case_obj.current = 7 + if case_obj.agent: print(f"Case {case}: started 'manufacture A' at {env.now:.2f}") + yield env.process(process.manufacture_a(case)) + 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: + with process.manufacturer_b.request() as request: + yield request + case_obj.state[7] += 1 + case_obj.current = 8 + if case_obj.agent: print(f"Case {case}: started 'manufacture B' at {env.now:.2f}") + yield env.process(process.manufacture_b(case)) + 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: + with process.packer_a.request() as request: + yield request + case_obj.state[8] += 1 + case_obj.current = 9 + if case_obj.agent: print(f"Case {case}: started 'pack A' at {env.now:.2f}") + yield env.process(process.pack_a(case)) + 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: + with process.packer_b.request() as request: + yield request + case_obj.state[9] += 1 + case_obj.current = 10 + if case_obj.agent: print(f"Case {case}: started 'pack B' at {env.now:.2f}") + yield env.process(process.pack_b(case)) + 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: + with process.packer_c.request() as request: + yield request + case_obj.state[10] += 1 + case_obj.current = 11 + if case_obj.agent: print(f"Case {case}: started 'pack C' at {env.now:.2f}") + yield env.process(process.pack_c(case)) + 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: + with process.delivery_service_a.request() as request: + yield request + case_obj.state[11] += 1 + case_obj.current = 12 + if case_obj.agent: print(f"Case {case}: started 'attempt delivery A' at {env.now:.2f}") + yield env.process(process.attempt_delivery_a(case)) + 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: + with process.delivery_service_b.request() as request: + yield request + case_obj.state[12] += 1 + case_obj.current = 13 + if case_obj.agent: print(f"Case {case}: started 'attempt delivery B' at {env.now:.2f}") + yield env.process(process.attempt_delivery_b(case)) + 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: + with process.delivery_service_c.request() as request: + yield request + case_obj.state[13] += 1 + case_obj.current = 14 + if case_obj.agent: print(f"Case {case}: started 'attempt delivery C' at {env.now:.2f}") + yield env.process(process.attempt_delivery_c(case)) + 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 + with process.system.request() as request: + yield request + case_obj.state[14] += 1 + case_obj.current = 15 + yield env.process(process.order_completed(case)) + 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 + +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 + +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) + 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)) + +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 = 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(1) + + space = [process, case] + activities = 16 + + business_env = environment.BusinessProcessEnv(space, activities) + print(business_env.observation_space.shape) + print(business_env.observation_space.sample()) + + state, _ = business_env.reset() + print(state) + print(business_env.current_state) + print(state['event']) + print(business_env.flatten_observation(state)) + agent.q_learning(space, activities) + +if __name__ == "__main__": + main()