#
vincentlu
2025-01-14 e1d1d6279ca95febb12f6ce476a54c5bfc30d93f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
package com.zy.acs.manager.core.service;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zy.acs.framework.common.Cools;
import com.zy.acs.manager.common.utils.CommonUtil;
import com.zy.acs.manager.core.domain.Lane;
import com.zy.acs.manager.core.domain.TaskPosDto;
import com.zy.acs.manager.manager.entity.*;
import com.zy.acs.manager.manager.enums.StatusType;
import com.zy.acs.manager.manager.enums.TaskStsType;
import com.zy.acs.manager.manager.enums.TaskTypeType;
import com.zy.acs.manager.manager.service.*;
import com.zy.acs.manager.system.service.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import java.util.*;
import java.util.stream.Collectors;
 
/**
 * Created by vincent on 8/12/2024
 */
@Slf4j
@Service
public class AllocateService {
 
    @Autowired
    private AgvService agvService;
    @Autowired
    private AgvDetailService agvDetailService;
    @Autowired
    private AgvModelService agvModelService;
    @Autowired
    private ConfigService configService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private CodeService codeService;
    @Autowired
    private StaService staService;
    @Autowired
    private LocService locService;
    @Autowired
    private LaneService laneService;
 
    /**
     * get available agv list which is idle
     */
    private List<Agv> getAvailableAgv() {
        List<Agv> result = new ArrayList<>();
        List<Agv> agvList = agvService.list(new LambdaQueryWrapper<Agv>().eq(Agv::getStatus, StatusType.ENABLE.val));
        Collections.shuffle(agvList);
        for (Agv agv : agvList) {
 
            // 1. without running tasks
            if (0 < taskService.count(new LambdaQueryWrapper<Task>()
                    .eq(Task::getAgvId, agv.getId())
                    .and(i ->
                            i.eq(Task::getTaskSts, TaskStsType.ASSIGN.val())
                            .or().eq(Task::getTaskSts, TaskStsType.PROGRESS.val())
                    )
            )) {
                continue;
            }
 
            // 2. in idle status
            if (!agvService.judgeEnable(agv.getId(), true)) {
                continue;
            }
 
            result.add(agv);
        }
 
        return result;
    }
 
    /**
     * 1.   判断task的起始点和目的点所在的巷道承载任务数量,
     *      如果数量已经达到负载,则判断负载任务的AGV是否还有空背篓,如果有则优先派发给它,
     *      如果没有了,那么则阻塞任务,直到该巷道释放
     * 2.   轮询空闲小车,目标是让每台小车都动起来
     *      判断逻辑:背篓数量最少的小车轮询的时候,优先级最高
     *
     *      it can break the limit of the number of agv backpack
     */
    public synchronized Agv execute(Task task) {
        List<Agv> availableAgvList = getAvailableAgv();
        if (Cools.isEmpty(availableAgvList)) {
//            log.warn("No available agv to assign the task[{}]", task.getSeqNum());
            return null;
        }
        List<String> availableAgvNos = availableAgvList.stream().map(Agv::getUuid).distinct().collect(Collectors.toList());
 
        Integer maxAgvCountInLane = configService.getVal("maxAgvCountInLane", Integer.class);
 
        // checkout lane
        Lane originLane = taskService.checkoutOriginLane(task);
        Lane destinationLane = taskService.checkoutDestinationLane(task);
 
 
        // allocate about origin
        List<String> availableAgvNosByOriLane = new ArrayList<>(availableAgvNos);
        if (null != originLane) {
            List<String> agvNosByOriLane = findAgvNosByLane(originLane);    // the agv list that had tasks in this lane
            // if full lane
            if (agvNosByOriLane.size() >= maxAgvCountInLane) {
 
                availableAgvNosByOriLane = Cools.getIntersection(agvNosByOriLane, availableAgvNos);
            }
        }
        // valid backpack limit
        availableAgvNosByOriLane = this.validBackpackLimit(availableAgvNosByOriLane);
 
 
        // allocate about destination
        List<String> availableAgvNosByDestLane = new ArrayList<>(availableAgvNos);
        if (null != destinationLane) {
            List<String> agvNosByDestLane = findAgvNosByLane(destinationLane);
            if (agvNosByDestLane.size() >= maxAgvCountInLane) {
 
                availableAgvNosByDestLane = Cools.getIntersection(agvNosByDestLane, availableAgvNos);
            }
        }
        availableAgvNosByDestLane = this.validBackpackLimit(availableAgvNosByDestLane);
 
        // valid
        if (Cools.isEmpty(availableAgvNosByOriLane)) {
            log.warn("No available agv to assign the task origin[{}]", task.getSeqNum());
            return null;
        }
        if (Cools.isEmpty(availableAgvNosByDestLane)) {
            log.warn("No available agv to assign the task destination[{}]", task.getSeqNum());
            return null;
        }
        List<String> actualAvailableAgvNos = Cools.getIntersection(availableAgvNosByOriLane, availableAgvNosByDestLane);
        if (Cools.isEmpty(actualAvailableAgvNos)) {
            log.warn("No available agv to assign the task[{}]", task.getSeqNum());
            return null;
        }
 
        // choose min number of running task
        actualAvailableAgvNos.sort(new Comparator<String>() {
            @Override
            public int compare(String agvNo1, String agvNo2) {
                return calcAllocateWeight(agvNo1, task) - calcAllocateWeight(agvNo2, task);
            }
        });
 
 
        if (null != originLane) {
            task.setOriLaneHash(originLane.getHashCode());
        }
        if (null != destinationLane) {
            task.setDestLaneHash(destinationLane.getHashCode());
        }
 
        return agvService.selectByUuid(actualAvailableAgvNos.stream().findFirst().orElse(null));
    }
 
    public List<String> findAgvNosByLane(Lane lane) {
        if (null == lane) {
            return new ArrayList<>();
        }
        List<Task> taskList = taskService.findRunningTasksByLaneHash(lane.getHashCode());
        if (Cools.isEmpty(taskList)) {
            return new ArrayList<>();
        }
        return taskList.stream()
                .map(task -> agvService.getById(task.getAgvId()).getUuid())
                .distinct()
                .collect(Collectors.toList());
    }
 
    private List<String> validBackpackLimit(List<String> agvNoList) {
        if (Cools.isEmpty(agvNoList)) {
            return new ArrayList<>();
        }
        return agvNoList.stream().filter(agvNo -> {
            Agv agv = agvService.selectByUuid(agvNo);
            AgvModel agvModel = agvModelService.getByAgvNo(agvNo);
            List<Task> transportTasks = taskService.findTransportTasksByAgv(agv.getId());
            return transportTasks.size() < agvModel.getBackpack();
        }).collect(Collectors.toList());
    }
 
    // calculate wight = backpack + distance
    private int calcAllocateWeight(String agvNo, Task task) {
        int weight = 0;
        Long agvId = agvService.getAgvId(agvNo);
 
        // backpack
        List<Task> transportTasks = taskService.findTransportTasksByAgv(agvId);
        if (!Cools.isEmpty(transportTasks)) {
            weight = weight + transportTasks.size() * 100000;
        }
 
        // distance
        // from
        AgvDetail agvDetail = agvDetailService.selectByAgvId(agvId);
        Code agvCurrCode = codeService.getCacheById(agvDetail.getRecentCode());
        Double[] fromPosition = new Double[]{agvCurrCode.getX(), agvCurrCode.getY()};
        // to
        Code firstCode = null;
        TaskTypeType typeType = TaskTypeType.get(task.getTaskTypeEl());
        switch (Objects.requireNonNull(typeType)) {
            case LOC_TO_LOC:
            case LOC_TO_STA:
                Loc oriLoc = locService.getById(task.getOriLoc());
                firstCode = codeService.getCacheById(oriLoc.getCode());
                break;
            case STA_TO_LOC:
            case STA_TO_STA:
                Sta oriSta = staService.getById(task.getOriSta());
                firstCode = codeService.getCacheById(oriSta.getCode());
                break;
            case TO_CHARGE:
            case TO_STANDBY:
            case MOVE:
                firstCode = codeService.getCacheById(task.getDestCode());
                break;
            default:
                firstCode = codeService.getCacheById(task.getDestCode());
                break;
        }
        assert null != firstCode;
        Double[] toPosition = new Double[]{firstCode.getX(), firstCode.getY()};
        // calculate distance
        weight = weight + CommonUtil.calcDistance(fromPosition, toPosition);
 
        // return opposite
        return -weight;
    }
 
    public Boolean validCapacityOfLane(String agvNo, Code code) {
        Lane lane = laneService.search(code.getData());
        if (null != lane) {
            Integer maxAgvCountInLane = configService.getVal("maxAgvCountInLane", Integer.class);
 
            List<String> agvNosByLane = this.findAgvNosByLane(lane);
            agvNosByLane.remove(agvNo);
            if (agvNosByLane.size() >= maxAgvCountInLane) {
                return false;
            }
        }
 
        return true;
    }
 
 
    // The Permutations and combinations for task
 
    public Double[] pac(Double[] currPosition, List<List<TaskPosDto>> list) {
        List<TaskPosDto> theFirstOne = list.get(0);
        List<TaskPosDto> theLastOne = list.get(list.size() - 1);
 
        if (list.size() == 1) {
            TaskPosDto head = theFirstOne.get(0);
            TaskPosDto tail = theFirstOne.get(theFirstOne.size() - 1);
 
            int distanceByHead = CommonUtil.calcDistance(currPosition, head.getXy());
            int distanceByTail = CommonUtil.calcDistance(currPosition, tail.getXy());
 
            if (distanceByTail < distanceByHead) {
                Collections.reverse(theFirstOne);
            }
 
        } else {
            TaskPosDto headOfFirst = theFirstOne.get(0);
            TaskPosDto tailOfFirst = theFirstOne.get(theFirstOne.size() - 1);
 
            TaskPosDto headOfLast = theLastOne.get(0);
            TaskPosDto tailOfLast = theLastOne.get(theLastOne.size() - 1);
 
            int distanceByHeadOfFirst = CommonUtil.calcDistance(currPosition, headOfFirst.getXy());
            int distanceByTailOfFirst = CommonUtil.calcDistance(currPosition, tailOfFirst.getXy());
 
            int distanceByHeadOfLast = CommonUtil.calcDistance(currPosition, headOfLast.getXy());
            int distanceByTailOfLast = CommonUtil.calcDistance(currPosition, tailOfLast.getXy());
 
            if (Math.min(distanceByHeadOfLast, distanceByTailOfLast) < Math.min(distanceByHeadOfFirst, distanceByTailOfFirst)) {
                Collections.reverse(list);
 
                if (distanceByTailOfLast < distanceByHeadOfLast) {
                    Collections.reverse(theLastOne);
                }
            } else {
                if (distanceByTailOfFirst < distanceByHeadOfFirst) {
                    Collections.reverse(theFirstOne);
                }
            }
        }
 
        theLastOne = list.get(list.size() - 1);
        return theLastOne.get(theLastOne.size() - 1).getXy();
    }
 
}