source: protocol/src/main/java/geniusweb/protocol/session/mopac2/MOPAC2State.java@ 52

Last change on this file since 52 was 52, checked in by ruud, 14 months ago

Fixed small issues in domaineditor.

File size: 12.2 KB
Line 
1package geniusweb.protocol.session.mopac2;
2
3import java.util.Arrays;
4import java.util.Collections;
5import java.util.HashMap;
6import java.util.LinkedList;
7import java.util.List;
8import java.util.Map;
9
10import com.fasterxml.jackson.annotation.JsonCreator;
11import com.fasterxml.jackson.annotation.JsonProperty;
12
13import geniusweb.actions.Action;
14import geniusweb.actions.PartyId;
15import geniusweb.inform.Agreements;
16import geniusweb.inform.OptIn;
17import geniusweb.progress.Progress;
18import geniusweb.progress.ProgressRounds;
19import geniusweb.protocol.NegoProtocol;
20import geniusweb.protocol.ProtocolException;
21import geniusweb.protocol.session.SessionResult;
22import geniusweb.protocol.session.SessionState;
23import geniusweb.protocol.session.mopac2.phase.OfferPhase;
24import geniusweb.protocol.session.mopac2.phase.OptInPhase;
25import geniusweb.protocol.session.mopac2.phase.Phase;
26import geniusweb.protocol.session.saop.SAOPSettings;
27import geniusweb.references.PartyWithProfile;
28
29/**
30 * Keeps track of the current {@link Phase}. Adds initializing stuff and
31 * time/deadline checking. This state does not contain connections, this assumes
32 * that someone else handles (i.e. {@link NegoProtocol} that and the connections
33 * with the parties and that negotiation events are just pumped in from there.
34 * <p>
35 * This object is a bit tricky. It has two states
36 * <ol>
37 * <li>The initial state, where phase=null and connections are being made to all
38 * parties. At this point, problems can not be handled nicely yet because there
39 * are no PartyId's for problematic parties
40 * <li>The working state, where all parties are connected and all problems can
41 * be connected to an offending party.
42 * </ol>
43 */
44public class MOPAC2State implements SessionState {
45
46 private final Phase phase; // maybe null while initializing
47 private final MOPAC2Settings settings;
48 private final Map<PartyId, PartyWithProfile> partyprofiles;
49 private final List<Action> actions;
50 private final Progress progress;
51
52 /**
53 * Creates the initial state from the given settings and progress=null
54 *
55 * @param settings the {@link SAOPSettings}
56 */
57 public MOPAC2State(MOPAC2Settings settings) {
58 this(null, Arrays.asList(), null, settings, Collections.emptyMap());
59
60 }
61
62 /**
63 * @param phase The Phase, or null if still initializing. Phase is
64 * set something only after we have connections.
65 * @param actions the legal actions that have been done in the
66 * negotiation. {@link PartyStates#getActions()} is
67 * called to collect the actions after the phase is
68 * finished.first action in list is the oldest. This
69 * will not contain actions that immediately led to an
70 * agreement, because {@link PartyStates} removes the
71 * actions that led to an agreement. This MUST NOT
72 * contain illegal actions. Parties doing illegal
73 * actions are killed and the offending action ends up
74 * in the stacktrace. Previous actions of crashed
75 * parties remain standing and valid.
76 * @param progress the {@link Progress} line. can be null if not yet
77 * known. null happens because during initialization
78 * phase the protocol first add all connections. During
79 * this time, parties may already enter the
80 * failed/exception state.
81 * @param settings the {@link SAOPSettings}
82 * @param partyprofiles map with the {@link PartyWithProfile} for connected
83 * parties. null is equivalent to an empty map.
84 */
85 @JsonCreator
86 protected MOPAC2State(@JsonProperty("phase") Phase phase,
87 @JsonProperty("actions") List<Action> actions,
88 @JsonProperty("progress") Progress progress,
89 @JsonProperty("settings") MOPAC2Settings settings,
90 @JsonProperty("partyprofiles") Map<PartyId, PartyWithProfile> partyprofiles) {
91 this.phase = phase;
92 this.actions = actions;
93 this.progress = progress;
94 this.settings = settings;
95 this.partyprofiles = partyprofiles;
96 }
97
98 /**
99 * Sets the progress for this session and initial phase. Must be called
100 * after all parties have been connected with
101 * {@link #with(PartyId, PartyWithProfile)}.
102 *
103 * @param newprogress the initial {@link Progress} typically matching the
104 * settings deadline object
105 * @param now current time ms since 1970
106 *
107 * @return state with the initial partystates , progress set.
108 */
109 public MOPAC2State initPhase(Progress newprogress, long now) {
110 if (progress != null || newprogress == null || phase != null) {
111 throw new IllegalArgumentException(
112 "progress must be null, newprogress must be not null and phase must be INIT");
113 }
114
115 PartyStates partyStates = new PartyStates(getPowers());
116 Phase firstPhase = new OfferPhase(partyStates,
117 now + getAvailablePhaseTime(newprogress, now),
118 settings.getVotingEvaluation());
119 return new MOPAC2State(firstPhase, actions, newprogress, settings,
120 partyprofiles);
121 }
122
123 @Override
124 public List<Action> getActions() {
125 return Collections.unmodifiableList(actions);
126 }
127
128 @Override
129 public Progress getProgress() {
130 return progress;
131 }
132
133 @Override
134 public Agreements getAgreements() {
135 return phase.getPartyStates().getAgreements();
136 }
137
138 @Override
139 public MOPAC2Settings getSettings() {
140 return settings;
141 }
142
143 public Map<PartyId, PartyWithProfile> getPartyProfiles() {
144 return Collections.unmodifiableMap(partyprofiles);
145 }
146
147 @Override
148 public boolean isFinal(long now) {
149 return phase != null && phase.isFinal(now) && !isNewPhasePossible(now);
150 }
151
152 @Override
153 public List<SessionResult> getResults() {
154 return Arrays.asList(new SessionResult(partyprofiles, getAgreements(),
155 Collections.emptyMap(), null));
156 }
157
158 /**
159 * @param progress the Progress that needs to be checked
160 * @param now current time ms since 1970
161 * @return the max possible duration in ms of a phase considering the
162 * progress.
163 */
164 private static Long getAvailablePhaseTime(Progress aprogress, long now) {
165 return Math.min(aprogress.getTerminationTime().getTime() - now,
166 Phase.PHASE_MAXTIME);
167 }
168
169 /**
170 * @param id the new {@link PartyId}
171 * @param partyprofile the {@link PartyWithProfile} that is associated with
172 * this state
173 * @return new {@link MOPAC2State} with the new party added. This call
174 * ignores the progress (does not check isFinal) because we uses
175 * this during the setup where the deadline is not yet relevant.
176 */
177 protected MOPAC2State with(PartyId id, PartyWithProfile partyprofile) {
178 if (phase != null)
179 throw new IllegalStateException(
180 "Adding connections only allowed while initializing");
181
182 Map<PartyId, PartyWithProfile> newprofiles = new HashMap<>(
183 partyprofiles);
184 newprofiles.put(id, partyprofile);
185 return new MOPAC2State(null, actions, progress, settings, newprofiles);
186 }
187
188 /**
189 * @param e the {@link ProtocolException} that occured
190 * @return a new state with the error set. You MUST have called
191 * {@link #initPhase(Progress, long)} before using this
192 */
193 public MOPAC2State with(ProtocolException e) {
194 return new MOPAC2State(phase.with(e), actions, progress, settings,
195 partyprofiles);
196 }
197
198 /**
199 * Start the next phase. If new phase is OfferPhase, we increase progress.
200 * Actions is reset to empty. does nothing if not
201 * {@link #isNewPhasePossible(long)}
202 *
203 * @param now current time
204 * @return new {@link MOPAC2State} with phase initialized for next phase.
205 */
206 public MOPAC2State nextPhase(long now) {
207 long remainingNegoTime = progress.getTerminationTime().getTime() - now;
208 Phase newphase = phase.next(now,
209 Math.min(remainingNegoTime, Phase.PHASE_MAXTIME));
210
211 return new MOPAC2State(newphase, actions, increment(progress, phase),
212 getSettings(), partyprofiles);
213 }
214
215 /**
216 * When this is called, all parties should have acted.
217 *
218 * @param now current time
219 * @return true if there are still &gt;2 parties active and we have enough
220 * time for a new phase.
221 */
222 public boolean isNewPhasePossible(long now) {
223 // System.out.println("phase=" + phase);
224 Progress newprogress = increment(progress, phase);
225 if (newprogress.isPastDeadline(now + Phase.PHASE_MINTIME))
226 return false;
227
228 return phase.getPartyStates().getNegotiatingParties().size() >= 2
229 && getAvailablePhaseTime(newprogress,
230 now) > Phase.PHASE_MINTIME;
231 }
232
233 public Phase getPhase() {
234 return phase;
235 }
236
237 /**
238 * Check if action is allowed. Add action to the list of actions. Notice,
239 * this does NOT check if we need to step to the next phase, because
240 * deciding that is also depending on time-outs.
241 *
242 * @param actor the actor that did this action. Can be used to check if
243 * action is valid. NOTICE caller has to make sure the current
244 * state is not final. MUST NOT be null.
245 * @param action the action that was proposed by actor. MUST NOT be null.
246 * @param now the current time in ms since 1970, see
247 * {@link System#currentTimeMillis()}
248 * @return new {@link MOPAC2State} with the action checked and registered.
249 * If the action is not allowed, the new state may be that the actor
250 * is in the exception list.
251 */
252
253 public MOPAC2State with(PartyId actor, Action action, long now) {
254 return new MOPAC2State(phase.with(actor, action, now), actions,
255 progress, settings, partyprofiles);
256 }
257
258 /**
259 * @param aprogress the progress that might need to be advanced
260 * @param aphase the phase
261 * @return the next progress. Progress round advances if phase is
262 * {@link OptIn}.
263 */
264 private static Progress increment(Progress aprogress, Phase aphase) {
265 if (aprogress instanceof ProgressRounds && aphase instanceof OptInPhase)
266 return ((ProgressRounds) aprogress).advance();
267 return aprogress;
268 }
269
270 /**
271 * @return the power of all parties as set in their parameters, default =1.
272 * bad power values (non-integer, or <1) are ignored.
273 */
274 private Map<PartyId, Integer> getPowers() {
275 Map<PartyId, Integer> map = new HashMap<>();
276 for (PartyId pid : partyprofiles.keySet()) {
277 Object power = partyprofiles.get(pid).getParty().getParameters()
278 .get("power");
279 if (power == null || !(power instanceof Integer)
280 || ((Integer) power) < 1)
281 power = 1;
282 map.put(pid, (Integer) power);
283 }
284 return map;
285 }
286
287 @Override
288 public String toString() {
289 return "MOPAC2State[" + phase + "," + settings + "," + partyprofiles
290 + "," + progress + "]";
291 }
292
293 /**
294 *
295 * @return a wrapped-up state, with all parties doen an action or kicked,
296 * and agreements collected
297 */
298 public MOPAC2State finishPhase() {
299 Phase newphase = phase.finish();
300 LinkedList<Action> newactions = new LinkedList<Action>(actions);
301 newactions.addAll(newphase.getPartyStates().getActions());
302 return new MOPAC2State(newphase, newactions, progress, settings,
303 partyprofiles);
304 }
305
306 @Override
307 public int hashCode() {
308 final int prime = 31;
309 int result = 1;
310 result = prime * result + ((actions == null) ? 0 : actions.hashCode());
311 result = prime * result
312 + ((partyprofiles == null) ? 0 : partyprofiles.hashCode());
313 result = prime * result + ((phase == null) ? 0 : phase.hashCode());
314 result = prime * result
315 + ((progress == null) ? 0 : progress.hashCode());
316 result = prime * result
317 + ((settings == null) ? 0 : settings.hashCode());
318 return result;
319 }
320
321 @Override
322 public boolean equals(Object obj) {
323 if (this == obj)
324 return true;
325 if (obj == null)
326 return false;
327 if (getClass() != obj.getClass())
328 return false;
329 MOPAC2State other = (MOPAC2State) obj;
330 if (actions == null) {
331 if (other.actions != null)
332 return false;
333 } else if (!actions.equals(other.actions))
334 return false;
335 if (partyprofiles == null) {
336 if (other.partyprofiles != null)
337 return false;
338 } else if (!partyprofiles.equals(other.partyprofiles))
339 return false;
340 if (phase == null) {
341 if (other.phase != null)
342 return false;
343 } else if (!phase.equals(other.phase))
344 return false;
345 if (progress == null) {
346 if (other.progress != null)
347 return false;
348 } else if (!progress.equals(other.progress))
349 return false;
350 if (settings == null) {
351 if (other.settings != null)
352 return false;
353 } else if (!settings.equals(other.settings))
354 return false;
355 return true;
356 }
357
358}
Note: See TracBrowser for help on using the repository browser.