/
TeacherBehaviour.java
172 lines (137 loc) · 6.37 KB
/
TeacherBehaviour.java
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
package behaviours;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
import agents.Availability;
import agents.Teacher;
import jade.core.AID;
import jade.core.behaviours.CyclicBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.UnreadableException;
import messages.FirstAssignationMessage;
import messages.InitMessage;
import messages.Message;
import messages.TeacherGroupChangeMessage;
import messages.TeacherGroupChangeRequestMessage;
/**
* This is the main teacher behaviour. The behaviour encapsulates the teacher
* logic, storing the availability days of the alumns.
*/
public class TeacherBehaviour extends CyclicBehaviour {
private static final long serialVersionUID = 4979147830188132019L;
private static int MAX_ALUMNS_PER_GROUP = 5;
public static int EXPECTED_ALUMN_COUNT = 20;
private static EnumSet<Availability> AVAILABLE_GROUPS = EnumSet
.of(Availability.MONDAY, Availability.TUESDAY, Availability.THURDSDAY,
Availability.FRIDAY);
/**
* This is the list of registered alumns and the group they're assigned to.
*/
private final HashMap<AID, Availability> groups;
private final Teacher teacher;
private final Random random;
private int alumnCount;
public HashMap<AID, Availability> getGroups() {
return new HashMap<AID, Availability>(this.groups);
}
public TeacherBehaviour(Teacher agent) {
super(agent);
this.groups = new HashMap<>();
this.teacher = agent;
this.random = new Random();
this.alumnCount = 0;
}
private Availability firstAssignation(AID alumn) {
if (this.groups.containsKey(alumn)) {
System.err.println("WARN: Requested first assignation for already registered alumn "
+ alumn);
return this.groups.get(alumn);
}
// TODO: This could be more optimized, for example, having the
// availabilityCount map cached
// Get the count of the current availabilities
final Map<Availability, Long> availabilityCount = this.groups.values().stream()
.collect(Collectors.groupingBy(a -> a, Collectors.counting()));
// Get the current available groups
final List<Availability> availableGroups = TeacherBehaviour.AVAILABLE_GROUPS.stream()
.filter(a -> availabilityCount.getOrDefault(a,
0l) < TeacherBehaviour.MAX_ALUMNS_PER_GROUP)
.collect(Collectors.toList());
// Pick a random one
final Availability result = availableGroups
.get(this.random.nextInt(availableGroups.size()));
this.groups.put(alumn, result);
return result;
}
private void printStartingStatus() {
System.out.println("\n\n\n========================================");
System.out.println("Starting");
System.out.println("========================================\n\n");
final SortedSet<AID> keys = new TreeSet<AID>(this.groups.keySet());
for (final AID key : keys) {
final Availability group = this.groups.get(key);
System.out.println(" * " + key.getLocalName() + ": " + group);
}
System.out.println("\n\n\n");
}
@Override
public void action() {
// We have to use a timeout to allow the WakerBehaviour to take place
final ACLMessage msg = this.teacher.blockingReceive(100);
if (msg == null) {
return;
}
final AID sender = msg.getSender();
Message message = null;
try {
message = (Message) msg.getContentObject();
} catch (final UnreadableException e) {
System.err.println("[" + this.myAgent.getAID() + "] Error receiving message from "
+ sender);
e.printStackTrace(System.err);
return;
}
System.err.println("INFO: [" + this.myAgent.getAID() + "] ReceiveMessage ("
+ message.getType() + ")");
switch (message.getType()) {
case FIRST_ASSIGNATION_REQUEST:
this.teacher.replyTo(msg,
new FirstAssignationMessage(this.firstAssignation(sender)));
this.alumnCount += 1;
assert this.alumnCount <= TeacherBehaviour.EXPECTED_ALUMN_COUNT;
if (TeacherBehaviour.EXPECTED_ALUMN_COUNT == this.alumnCount) {
this.printStartingStatus();
this.teacher.sendMessageToType("alumn", new InitMessage());
}
return;
case TEACHER_GROUP_CHANGE_REQUEST:
final TeacherGroupChangeRequestMessage requestMessage = (TeacherGroupChangeRequestMessage) message;
assert requestMessage.fromAlumn.equals(sender);
// Ensure those alumns haven't been changed previously
assert this.groups.get(requestMessage.fromAlumn) == requestMessage.fromGroup
&& this.groups.get(requestMessage.toAlumn) == requestMessage.toGroup;
System.out.println("Group change: " + requestMessage.fromAlumn.getLocalName()
+ " <-> " + requestMessage.toAlumn.getLocalName());
this.groups.put(requestMessage.fromAlumn, requestMessage.toGroup);
this.groups.put(requestMessage.toAlumn, requestMessage.fromGroup);
// We don't send the message to every alumn, we send it to both
// implicated alumns, and they'll take care to forward it to
// everyone else
final Message content = new TeacherGroupChangeMessage(requestMessage.fromAlumn,
requestMessage.toAlumn, requestMessage.fromGroup, requestMessage.toGroup);
this.teacher.replyTo(msg, content);
this.teacher.sendMessage(requestMessage.toAlumn, content, ACLMessage.INFORM);
return;
default:
System.err.println("ERROR: Unexpected message of type " + message.getType()
+ " received in TeacherBehaviour. Sender: " + sender + "; Receiver: "
+ this.teacher.getAID());
return;
}
}
}