You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

634 lines
30 KiB

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))