from fastapi import APIRouter from pydantic import BaseModel, constr from starlette.requests import Request from helper import respond_to from models.user import User, User_Pydantic from models.order import Order from models.task import Task, Subtask from models.assignment import Assignment from models.warehouse import Vacancy from logic.formmodel import UserM, TaskM from tortoise.queryset import QuerySet, Q from conf import setting from tortoise.transactions import in_transaction from helper.utils import timezone_now from typing import Optional import json from algorithm.tsp import getItinerarys, getPoint2PointDistance order_router = APIRouter(prefix='/order') class OrderM(BaseModel): orderid: str owner: UserM # 是否是取预订单 is_get: bool = False ordertime: str tasks: Optional[list[TaskM]] = [] class OrderidModel(BaseModel): order_id: Optional[str] class SeqModel(BaseModel): order_id: Optional[str] seq: Optional[int] = 0 @order_router.get("/") def read_root(): # print("getPoint2PointDistance: ", getPoint2PointDistance(["C1", "C4"])) return {"Hello": "World"} @order_router.put('/seq/{order_id}', summary='调度系统管理员手动调整任务序列') async def update(request: Request, model: SeqModel, order_id: str): # print("order_id: ", order_id) # print("model seq: ", model.seq) order = await Order.get_or_none(orderid = order_id) if not order: return respond_to(404, "Cannot find Order id", data={"state": "failed to find tartget order id {}".format(order_id)}) # print("order: ", order.as_json()) await order.fetch_related("tasks") for task in order.tasks: # print("task: ", task) task.sequence = model.seq await task.save(update_fields=['sequence']) await task.fetch_related("subtasks") for subtask in task.subtasks: # print("subtask: ", subtask) subtask.sequence = model.seq await subtask.save(update_fields=['sequence']) order.sequence = model.seq await order.save(update_fields=['sequence']) assignments = await Assignment.filter(orderid = order_id, is_active = True, is_valid = True) for assignment in assignments: #processing assignment cannot be canceled if assignment and not assignment.is_processing: # print("assignment: ", assignment) assignment.sequence = model.seq await assignment.save(update_fields=['sequence']) return respond_to(data={"state": "success"}) @order_router.put('/cancel/{order_id}', summary='中控平台取消订单') async def update(request: Request, model: OrderidModel, order_id: str): # print("order_id: ", order_id) order = await Order.get_or_none(orderid = order_id) if not order: return respond_to(404, "Cannot find Order id", data={"state": "failed to find tartget order id {}".format(order_id)}) # print("order: ", order.as_json()) await order.fetch_related("tasks") for task in order.tasks: # print("task: ", task) task.is_canceled = True task.cancel_time = timezone_now() await task.save(update_fields=['is_canceled', 'cancel_time']) await task.fetch_related("subtasks") for subtask in task.subtasks: # print("subtask: ", subtask) subtask.is_canceled = True subtask.cancel_time = timezone_now() await subtask.save(update_fields=['is_canceled', 'cancel_time']) order.is_canceled = True order.cancel_time = timezone_now() await order.save(update_fields=['is_canceled', 'cancel_time']) assignments = await Assignment.filter(orderid = order_id, is_active = True, is_valid = True) for assignment in assignments: #processing assignment cannot be canceled if assignment and not assignment.is_processing: # print("assignment: ", assignment) assignment.is_canceled = True assignment.cancel_time = timezone_now() await assignment.save(update_fields=['is_canceled', 'cancel_time']) return respond_to(data={"state": "success"}) # async def updateLoad(subTask, assignment, last): # assignment.last = last # await assignment.subtasks.add(subTask) # # await assignment.fetch_related("subtasks") # # print("subTask: ", subTask.as_json()) # if not assignment.subtasksj: # # print("Create new subtasks: ", subTask.id, assignment.subtasksj, not assignment.subtasksj) # assignment.subtasksj = [subTask.as_json()] # else: # # print("Add Exist subtasks: ", subTask.id) # subtasksj = assignment.subtasksj # filtered = filter(lambda obj: obj["id"] == "{}".format(subTask.id), subtasksj) # filteredList = list(filtered) # # print("filteredList: ", filteredList, len(filteredList)) # if len(filteredList) < 1: # # print("Don't Exist!: ", subTask.id) # subtasksj.append(subTask.as_json()) # assignment.subtasksj = subtasksj # else: # print("subTask.id Already Exist!: ", subTask.id) # #for A standard # if 1 == subTask.typetask: # assignment.loada = assignment.loada + subTask.quantity # #for B standard # if 3 == subTask.typetask: # assignment.loadb = assignment.loadb + subTask.quantity # #for C standard # if 5 == subTask.typetask: # assignment.loada = assignment.loadc + subTask.quantity # #for D standard # if 7 == subTask.typetask: # assignment.loadb = assignment.loadd + subTask.quantity # #for E standard # if 9 == subTask.typetask: # assignment.loade = assignment.loade + subTask.quantity # # p = await User_Pydantic.from_tortoise_orm(await User.get(name="2346")) # # p = await User_Pydantic.from_tortoise_orm(await User.get(name="2346")) # # print("One Event:", p) # # print("Json subTask.owner: ", subTask.owner.as_json()) # owner = await User_Pydantic.from_tortoise_orm(await subTask.owner) # assignment.ownerj = owner.json() # await assignment.save() async def updateLoad(subTask, assignment, last): assignment.last = last await assignment.subtasks.add(subTask) #for A standard if 1 == subTask.typetask: assignment.loada = assignment.loada + subTask.quantity #for B standard if 3 == subTask.typetask: assignment.loadb = assignment.loadb + subTask.quantity #for C standard if 5 == subTask.typetask: assignment.loada = assignment.loadc + subTask.quantity #for D standard if 7 == subTask.typetask: assignment.loadb = assignment.loadd + subTask.quantity #for E standard if 9 == subTask.typetask: assignment.loade = assignment.loade + subTask.quantity await assignment.save() @order_router.post('/add', summary='中控平台添加订单') async def create(request: Request, orderm: OrderM): # print("orderm: ", orderm) print("orderm owner: ", orderm.owner.username, orderm.owner) async with in_transaction() as conn: owner = None try: owner = await User.get(id = orderm.owner.id) print("User existing: ", owner) #update user info if owner.username != orderm.owner.username: owner.username = orderm.owner.username if owner.name != orderm.owner.name: owner.name = orderm.owner.name if owner.phone != orderm.owner.phone: owner.phone = orderm.owner.phone if owner.email != orderm.owner.email: owner.email = orderm.owner.email if owner.is_locked != orderm.owner.is_locked: owner.is_locked = orderm.owner.is_locked await owner.save() except: owner = await User.create(**orderm.owner.dict(), connection=conn) print("User not existing: ", owner) if orderm.is_get: #generate task to get things at shelf Vacany order = await Order.get_or_none(orderid = orderm.orderid) if order: print("Existing Order {}".format(order.orderid)) if not order.is_done: msg = "order {} is not Done!".format(order.orderid) print(msg) return respond_to(code=406, desc="出错", data={"state": msg}) orderOwner = await order.owner if orderOwner.id == owner.id: #generate tasks for get things vacancies = await Vacancy.filter(orderids__contains = [order.orderid], is_shelf = True, is_valid = False, is_active = True) # print("vacancies: ", vacancies) if len(vacancies) > 0: # sort vacancy number as itinerary itinerary = [ obj.number for obj in vacancies ] itinerary.sort() # print("itinerary: ", itinerary) assignment = await Assignment.create( orderid = order.orderid, owner = orderOwner, # set priority level higher sequence = 1, ordertime = order.ordertime, agvid = setting.AGVID, itinerary = itinerary, loade = 1, ) # print("assignment: ", assignment) for vacancy in vacancies: subTask = await Subtask.create( owner = orderOwner, #取预约领用的任务类型属于非标标准品 typetask = 9, orderid = order.orderid, #取预约领用的动作类型属于现场领用 action = 5, ordertime = order.ordertime, name = "取预约领用,订单号{}".format(order.orderid), quantity = 1, coordinates = vacancy.number, traynum = vacancy.traynum, connection=conn ) # print("subTask: ", subTask) await assignment.subtasks.add(subTask) else: msg = "Order Owner {} and Order Owner {} don't match".format(orderOwner.id, owner.id) print(msg) return respond_to(code=404, desc="出错", data={"state": msg}) else: msg = "Cannot find corresponding order {}".format(order.orderid) print(msg) return respond_to(code=404, desc="出错", data={"state": msg}) else: #create new order = await Order.create( owner = owner, orderid = orderm.orderid, ordertime = orderm.ordertime, connection=conn ) #filter coordinate label list #add the start point at the beginning coordinates = {"A0": []} tasks = orderm.tasks for task in tasks: print("task: ", task.name, task.action, task.typetask) taskn = await Task.create( owner = owner, typetask = task.typetask, orderid = order.orderid, action = task.action, ordertime = orderm.ordertime, name = task.name, quantity = task.quantity, coordinates = task.coordinates, traynum = task.traynum, connection=conn ) await order.tasks.add(taskn) #split into subtasks accodring to simple rules if taskn.typetask < 9: if taskn.quantity > setting.AGVCAP[taskn.typetask]: # split standard task into parts as AGV capacity limit, recursively async def splitSubtask(taskn: Task, quantity): curQuantity = quantity - setting.AGVCAP[taskn.typetask] print("curQuantity ", curQuantity ) subTask = await Subtask.create( owner = owner, typetask = taskn.typetask, orderid = taskn.orderid, action = taskn.action, ordertime = taskn.ordertime, name = taskn.name, quantity = setting.AGVCAP[taskn.typetask], coordinates = taskn.coordinates, traynum = taskn.traynum, connection=conn ) await taskn.subtasks.add(subTask) if curQuantity > setting.AGVCAP[taskn.typetask]: print("curQuantity continue split: ", curQuantity, setting.AGVCAP[taskn.typetask] ) # continue split await splitSubtask(taskn, curQuantity) else: print("curQuantity is OK: ", curQuantity) #add remainning stock into subtask subTask = await Subtask.create( owner = owner, typetask = taskn.typetask, orderid = taskn.orderid, action = taskn.action, ordertime = taskn.ordertime, name = taskn.name, quantity = curQuantity, coordinates = taskn.coordinates, traynum = taskn.traynum, connection=conn ) await taskn.subtasks.add(subTask) quantity = taskn.quantity print("quantity is exceed ", quantity ) await splitSubtask(taskn, quantity) else: print("quantity is OK! ", taskn.quantity) subTask = await Subtask.create( owner = owner, typetask = taskn.typetask, orderid = taskn.orderid, action = taskn.action, ordertime = taskn.ordertime, name = taskn.name, quantity = taskn.quantity, coordinates = taskn.coordinates, traynum = taskn.traynum, connection=conn ) await taskn.subtasks.add(subTask) else: #non standard will split into single task subTask = await Subtask.create( owner = owner, typetask = taskn.typetask, orderid = taskn.orderid, action = taskn.action, ordertime = taskn.ordertime, name = taskn.name, quantity = taskn.quantity, coordinates = taskn.coordinates, traynum = taskn.traynum, connection=conn ) await taskn.subtasks.add(subTask) try: # shallow copy child task list coordinate = coordinates[taskn.coordinates] coordinate.append(taskn) except: coordinates[taskn.coordinates] = [taskn] labels = list(coordinates.keys()) # print("Label Keys: ", labels) #add the start point at the beginning # get short path only when more than 2 items in labels if len(labels) > 2: trip = getItinerarys(labels) print("Order trip: ", trip) assignments = [] for key in trip: print("key: ", key) taskList = coordinates[key] for taskl in taskList: print("tasklist: ", taskl) # need fetch child manytomany relations await taskl.fetch_related("subtasks") for subTask in taskl.subtasks: print("\tsubTask: ", subTask) #merge subtask into assignment package if len(assignments) > 0: #sort the max available occupancy by greedy algorithm if 1 == subTask.typetask: assignments.sort(key=lambda assignment: assignment.loadb, reverse=True) assignments.sort(key=lambda assignment: assignment.loadc, reverse=True) assignments.sort(key=lambda assignment: assignment.loadd, reverse=True) assignments.sort(key=lambda assignment: assignment.loade, reverse=True) #main sort at last assignments.sort(key=lambda assignment: assignment.loada, reverse=True) elif 3 == subTask.typetask: assignments.sort(key=lambda assignment: assignment.loada, reverse=True) assignments.sort(key=lambda assignment: assignment.loadc, reverse=True) assignments.sort(key=lambda assignment: assignment.loadd, reverse=True) assignments.sort(key=lambda assignment: assignment.loade, reverse=True) #main sort at last assignments.sort(key=lambda assignment: assignment.loadb, reverse=True) elif 5 == subTask.typetask: assignments.sort(key=lambda assignment: assignment.loada, reverse=True) assignments.sort(key=lambda assignment: assignment.loadb, reverse=True) assignments.sort(key=lambda assignment: assignment.loadd, reverse=True) assignments.sort(key=lambda assignment: assignment.loade, reverse=True) #main sort at last assignments.sort(key=lambda assignment: assignment.loadc, reverse=True) elif 7 == subTask.typetask: assignments.sort(key=lambda assignment: assignment.loada, reverse=True) assignments.sort(key=lambda assignment: assignment.loadb, reverse=True) assignments.sort(key=lambda assignment: assignment.loadc, reverse=True) assignments.sort(key=lambda assignment: assignment.loade, reverse=True) #main sort at last assignments.sort(key=lambda assignment: assignment.loadd, reverse=True) elif 9 == subTask.typetask: assignments.sort(key=lambda assignment: assignment.loada, reverse=True) assignments.sort(key=lambda assignment: assignment.loadb, reverse=True) assignments.sort(key=lambda assignment: assignment.loadc, reverse=True) assignments.sort(key=lambda assignment: assignment.loadd, reverse=True) #main sort at last assignments.sort(key=lambda assignment: assignment.loade, reverse=True) else: print("Tasktype is not match: ", subTask.typetask) isDone = False for assignment in assignments: searchDepth = getPoint2PointDistance([key, assignment.last]) if setting.MAXSEARCHDEPTH > searchDepth: print("Within MAXSEARCHDEPTH: {}, {} -> {}".format(searchDepth, key, assignment.last)) if 1 == subTask.typetask: if subTask.quantity + assignment.loada <= setting.AGVCAP[1]: await updateLoad(subTask, assignment, key) isDone = True break elif 3 == subTask.typetask: if subTask.quantity + assignment.loadb <= setting.AGVCAP[3]: await updateLoad(subTask, assignment, key) isDone = True break elif 5 == subTask.typetask: if subTask.quantity + assignment.loadc <= setting.AGVCAP[5]: await updateLoad(subTask, assignment, key) isDone = True break elif 7 == subTask.typetask: if subTask.quantity + assignment.loadd <= setting.AGVCAP[7]: await updateLoad(subTask, assignment, key) isDone = True break elif 9 == subTask.typetask: if subTask.quantity + assignment.loade <= setting.AGVCAP[9] and assignment.loade < 1: # only add non standard once await updateLoad(subTask, assignment, key) isDone = True break else: print("Beyond MAXSEARCHDEPTH: {}, {} -> {}".format(searchDepth, key, assignment.last)) if not isDone: #if above is not match then create new one assignment = await Assignment.create( orderid = subTask.orderid, owner = owner, sequence = subTask.sequence, ordertime = subTask.ordertime, agvid = setting.AGVID, itinerary = trip, last = key, connection=conn ) await updateLoad(subTask, assignment, key) assignments.append(assignment) else: #empty assignment create new assignment = await Assignment.create( orderid = subTask.orderid, owner = owner, sequence = subTask.sequence, ordertime = subTask.ordertime, agvid = setting.AGVID, itinerary = trip, last = key, connection=conn ) await updateLoad(subTask, assignment, key) assignments.append(assignment) else: assignments = [] # add the start point at the beginning labels.append("A0") for key in labels: print("key: ", key) taskList = coordinates[key] for taskl in taskList: print("tasklist: ", taskl) # need fetch child manytomany relations await taskl.fetch_related("subtasks") for subTask in taskl.subtasks: print("\tsubTask: ", subTask) if len(assignments) > 0: isDone = False for assignment in assignments: if 1 == subTask.typetask: if subTask.quantity + assignment.loada <= setting.AGVCAP[1]: await updateLoad(subTask, assignment, key) isDone = True break elif 3 == subTask.typetask: if subTask.quantity + assignment.loadb <= setting.AGVCAP[3]: await updateLoad(subTask, assignment, key) isDone = True break elif 5 == subTask.typetask: if subTask.quantity + assignment.loadc <= setting.AGVCAP[5]: await updateLoad(subTask, assignment, key) isDone = True break elif 7 == subTask.typetask: if subTask.quantity + assignment.loadd <= setting.AGVCAP[7]: await updateLoad(subTask, assignment, key) isDone = True break elif 9 == subTask.typetask: if subTask.quantity + assignment.loade <= setting.AGVCAP[9] and assignment.loade < 1: # only add non standard once await updateLoad(subTask, assignment, key) isDone = True break if not isDone: #if above is not match then create new one assignment = await Assignment.create( orderid = subTask.orderid, owner = owner, sequence = subTask.sequence, ordertime = subTask.ordertime, agvid = setting.AGVID, itinerary = labels, last = key, connection=conn ) await updateLoad(subTask, assignment, key) assignments.append(assignment) else: assignment = await Assignment.create( orderid = subTask.orderid, owner = owner, sequence = subTask.sequence, ordertime = subTask.ordertime, agvid = setting.AGVID, itinerary = labels, last = key, connection=conn ) await updateLoad(subTask, assignment, key) assignments.append(assignment) return respond_to(data={"state": "success"}) @order_router.get('/list', summary="列出所有订单") async def index(request: Request, page_no: int = 1, page_size: int = 20): """ 列出所有订单 :param page_no: 1 :param page_size: 20 :return: """ offset = (page_no - 1) * page_size query = QuerySet(Order).filter(is_canceled=False, is_valid=True, is_active=True) count = await query.count() orders = await query.limit(page_size).offset(offset) return respond_to(code=200, data=dict(count=count, data=orders))