View Javadoc

1   /*
2    * ConfirmedProductionHandler.java Created @ Jul 12, 2004
3    * 
4    * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628 BX
5    * Delft, the Netherlands. All rights reserved.
6    * 
7    * See for project information <a href="http://www.simulation.tudelft.nl/">
8    * www.simulation.tudelft.nl </a>.
9    * 
10   * The source code and binary code of this software is proprietary information
11   * of Delft University of Technology.
12   */
13  
14  package org.gscg.singleuser.handlers;
15  
16  import java.lang.reflect.Field;
17  import java.rmi.RemoteException;
18  import java.util.Calendar;
19  import java.util.Collections;
20  import java.util.HashMap;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import nl.tudelft.simulation.actor.Actor;
27  import nl.tudelft.simulation.content.HandlerInterface;
28  import nl.tudelft.simulation.dsol.experiment.TimeUnit;
29  import nl.tudelft.simulation.dsol.experiment.TimeUnitInterface;
30  import nl.tudelft.simulation.event.Event;
31  import nl.tudelft.simulation.event.EventInterface;
32  import nl.tudelft.simulation.event.EventListenerInterface;
33  import nl.tudelft.simulation.event.EventProducer;
34  import nl.tudelft.simulation.event.EventType;
35  import nl.tudelft.simulation.language.reflection.ClassUtil;
36  import nl.tudelft.simulation.logger.Logger;
37  import nl.tudelft.simulation.supplychain.content.ProductionOrder;
38  import nl.tudelft.simulation.supplychain.handlers.ProductionOrderHandler;
39  
40  import org.gscg.common.gui.exceptions.ReceivedUnknownEventException;
41  import org.gscg.common.interactionlayer.AnnounceInterface;
42  import org.gscg.common.interactionlayer.ThreadedEventProducer;
43  import org.gscg.common.interactionlayer.timecontrol.GlobalRowOrColumnNumber;
44  import org.gscg.singleuser.interactionlayer.SingleUserInteractionLayerInterface;
45  import org.gscg.singleuser.interactionlayer.dataobjects.DateIntData;
46  import org.gscg.singleuser.interactionlayer.dataobjects.ProducedOrderDayData;
47  import org.gscg.singleuser.interactionlayer.dataobjects.ProducedOrderMonthData;
48  import org.gscg.singleuser.interactionlayer.dataobjects.ProducedOrderWeekData;
49  import org.gscg.singleuser.interactionlayer.dataobjects.ProductionStartedData;
50  import org.gscg.singleuser.production.GameDelayProductionService;
51  
52  /***
53   * Updates the number of produced products based on the day in the interactive
54   * simulation. Whenever a production order is received or is handled,
55   * accordingly the number is updated at the client side gui. This handler is
56   * used by the PRODUCTION PANEL of a client-side gui.
57   * <p>
58   * Copyright (c) 2003-2005 Delft University of Technology, Jaffalaan 5, 2628 BX
59   * Delft, the Netherlands. All rights reserved.
60   * 
61   * See for project information <a href="http://www.simulation.tudelft.nl/">
62   * www.simulation.tudelft.nl </a>.
63   * 
64   * The source code and binary code of this software is proprietary information
65   * of Delft University of Technology.
66   * 
67   * @author <a
68   *         href="http://www.tbm.tudelft.nl/webstaf/stijnh/index.htm">Stijn-Pieter
69   *         van Houten </a>
70   * @version $Revision: 1.3 $ $Date: 2005/08/10 11:23:30 $
71   * @since 1.0.0
72   */
73  public class ConfirmedProductionHandler extends EventProducer implements
74  		EventListenerInterface, AnnounceInterface
75  {
76  	/*** the serial version uid */
77  	private static final long serialVersionUID = 13L;
78  
79  	/*** the map with content handlers of an actor */
80  	private static Field handlerField = null;
81  
82  	static
83  	{
84  		try
85  		{
86  			ConfirmedProductionHandler.handlerField = ClassUtil.resolveField(
87  					Actor.class, "contentHandlers");
88  			handlerField.setAccessible(true);
89  		} catch (Exception exception)
90  		{
91  			Logger.severe(ThreadedEventProducer.class, "<clinit>", exception);
92  		}
93  	}
94  
95  	/*** the event type for the amount of orders per product per day */
96  	public static final EventType EVENT_NUMBER_ORDERPRODUCED_DAY = new EventType(
97  			"EVENT_NUMBER_ORDERPRODUCED_DAY");
98  
99  	/*** the event type for the amount of orders per product per week */
100 	public static final EventType EVENT_NUMBER_ORDERPRODUCED_WEEK = new EventType(
101 			"EVENT_NUMBER_ORDERPRODUCED_WEEK");
102 
103 	/*** the event type for the amount of produced orders per product per month */
104 	public static final EventType EVENT_NUMBER_ORDERPRODUCED_MONTH = new EventType(
105 			"EVENT_NUMBER_ORDERPRODUCED_MONTH");
106 
107 	/*** the event type to update all the produced order */
108 	public static final EventType EVENT_NUMBER_ORDERPRODUCED_UPDATE = new EventType(
109 			"EVENT_NUMBER_ORDERPRODUCED_UPDATE");
110 
111 	/*** the owner */
112 	private SingleUserInteractionLayerInterface owner = null;
113 
114 	/*** the product map per day */
115 	private Map productDayMap = new HashMap();
116 
117 	/*** the product map per week */
118 	private Map productWeekMap = new HashMap();
119 
120 	/*** the product map per month */
121 	private Map productMonthMap = new HashMap();
122 
123 	/*** indicates the current day */
124 	private int currentDay = 0;
125 
126 	/*** indicates the current week */
127 	private int currentWeek = 0;
128 
129 	/*** indicates the current month */
130 	private int currentMonth = 0;
131 
132 	/*** used when the runlength of a simulation run spans two calendar years */
133 	private int additionalDays = 0;
134 
135 	/*** used when the runlength of a simulation run spans two calendar years */
136 	private int additionalWeeks = 0;
137 
138 	/*** used when the runlength of a simulation run spans two calendar years */
139 	private int additionalMonths = 0;
140 
141 	/*** the events to subscribe the owner to */
142 	private EventType[] eventsToSubscribeOwnerTo = {
143 			ConfirmedProductionHandler.EVENT_NUMBER_ORDERPRODUCED_DAY,
144 			ConfirmedProductionHandler.EVENT_NUMBER_ORDERPRODUCED_WEEK,
145 			ConfirmedProductionHandler.EVENT_NUMBER_ORDERPRODUCED_MONTH};
146 
147 	/*** the history of the fired events */
148 	private Map history = Collections.synchronizedMap(new HashMap());
149 
150 	/***
151 	 * constructs a new ConfirmedOrderHandler
152 	 * 
153 	 * @param owner the owner
154 	 */
155 	public ConfirmedProductionHandler(
156 			final SingleUserInteractionLayerInterface owner)
157 	{
158 		super();
159 		this.owner = owner;
160 		try
161 		{
162 			// set the week and year
163 			Calendar calendar = Calendar.getInstance();
164 			double simTime = this.owner.getSimulator().getSimulatorTime();
165 
166 			// convert the simtime
167 			double convertedTime = TimeUnit.convert(simTime, this.owner
168 					.getSimulator().getReplication().getRunControl()
169 					.getTreatment().getTimeUnit(),
170 					TimeUnitInterface.MILLISECOND);
171 
172 			double startTime = this.owner.getSimulator().getReplication()
173 					.getRunControl().getTreatment().getStartTime();
174 			calendar.setTimeInMillis((long) (convertedTime + startTime));
175 		} catch (RemoteException remoteException)
176 		{
177 			Logger.severe(this, "ConfirmedOrderHandler", remoteException);
178 		}
179 
180 		// start listening to production started events
181 		try
182 		{
183 			Map contentHandlers = (Map) ConfirmedProductionHandler.handlerField
184 					.get(this.owner.getOwner());
185 
186 			Set handlers = new HashSet();
187 			if (contentHandlers.containsKey(ProductionOrder.class))
188 			{
189 				handlers.addAll((Set) contentHandlers
190 						.get(ProductionOrder.class));
191 			}
192 			if (handlers.size() != 0)
193 			{
194 				Iterator i = handlers.iterator();
195 				while (i.hasNext())
196 				{
197 					HandlerInterface handler = (HandlerInterface) i.next();
198 					// Now we invoke the business logic on the handler
199 					if (handler instanceof ProductionOrderHandler)
200 					{
201 						ProductionOrderHandler productionHandler = (ProductionOrderHandler) handler;
202 						Map services = productionHandler.getProduction()
203 								.getProductionServices();
204 						for (Iterator it = services.keySet().iterator(); it
205 								.hasNext();)
206 						{
207 							Object service = services.get(it.next());
208 							if (service instanceof GameDelayProductionService)
209 							{
210 								((GameDelayProductionService) service)
211 										.getEventProducer()
212 										.addListener(
213 												this,
214 												GameDelayProductionService.EVENT_PRODUCTION_STARTED);
215 							}
216 						}
217 					}
218 				}
219 			}
220 
221 		} catch (Exception exception)
222 		{
223 			Logger.severe(this, "<init>", exception);
224 			exception.printStackTrace();
225 		}
226 
227 		try
228 		{
229 			// subscribe the owner to events fired from this object
230 			// also add the necessary announce events
231 			for (int i = 0; i < this.eventsToSubscribeOwnerTo.length; i++)
232 			{
233 				this.addListener(this.owner, this.eventsToSubscribeOwnerTo[i]);
234 				this.owner.addEventType(this.eventsToSubscribeOwnerTo[i]);
235 			}
236 
237 			this.owner
238 					.addEventTypeToAnnounceList(
239 							ConfirmedProductionHandler.EVENT_NUMBER_ORDERPRODUCED_UPDATE,
240 							this);
241 		} catch (RemoteException remoteException)
242 		{
243 			Logger.severe(this, "<init>", remoteException);
244 		}
245 
246 		// initialize the history
247 		this.history.put(new String("day"), new HashMap());
248 		this.history.put(new String("week"), new HashMap());
249 		this.history.put(new String("month"), new HashMap());
250 	}
251 
252 	/***
253 	 * @see nl.tudelft.simulation.event.EventListenerInterface#notify(nl.tudelft.simulation.event.EventInterface)
254 	 */
255 	public void notify(final EventInterface event) throws RemoteException
256 	{
257 		if (event.getType().equals(
258 				GameDelayProductionService.EVENT_PRODUCTION_STARTED))
259 		{
260 			ProductionStartedData data = (ProductionStartedData) event
261 					.getContent();
262 			Calendar calendar = Calendar.getInstance();
263 
264 			DateIntData date = DateIntData.makeDateIntData(data.getEndDate(),
265 					this.owner.getOwner().getDEVSSimulator());
266 			calendar.set(date.getYear(), date.getMonth(), date.getDay());
267 
268 			Calendar startCalendar = Calendar.getInstance();
269 			startCalendar.setTimeInMillis(GlobalRowOrColumnNumber
270 					.getStartTime());
271 
272 			// calculcate difference between startCalendar and order date
273 			int yearDiffence = calendar.get(Calendar.YEAR)
274 					- startCalendar.get(Calendar.YEAR);
275 			// reset old additional values
276 			this.additionalDays = 0;
277 			this.additionalWeeks = 0;
278 			this.additionalMonths = 0;
279 
280 			while (yearDiffence > 0)
281 			{
282 				startCalendar.set(Calendar.YEAR, startCalendar
283 						.get(Calendar.YEAR) + 1);
284 				this.additionalDays = startCalendar
285 						.getActualMaximum(Calendar.DAY_OF_YEAR);
286 				this.additionalWeeks += 52;
287 				this.additionalMonths += 12;
288 
289 				yearDiffence--;
290 			}
291 
292 			// handle per day
293 			this.handlePerDay(data, calendar);
294 
295 			// handle per week
296 			this.handlePerWeek(data, calendar);
297 
298 			// handle per month
299 			this.handlePerMonth(data, calendar);
300 
301 			return;
302 		}
303 		new ReceivedUnknownEventException(this, "notify", event.getType());
304 	}
305 
306 	/***
307 	 * handles an orderconfirmation per day
308 	 * 
309 	 * @param data the data
310 	 * @param calendar the calendar
311 	 */
312 	private void handlePerDay(final ProductionStartedData data,
313 			final Calendar calendar)
314 	{
315 		int day = calendar.get(Calendar.DAY_OF_YEAR);
316 
317 		// we compensate extra years
318 		day += this.additionalDays;
319 
320 		if (this.currentDay != day)
321 		{
322 			this.currentDay = day;
323 		}
324 
325 		double value = Double.NaN;
326 		if (!this.productDayMap.containsKey(data.getOrder().getProduct()
327 				.getName()))
328 		{
329 			Map map = new HashMap();
330 			map.put("" + this.currentDay, new Double(data.getOrder()
331 					.getAmount()));
332 			this.productDayMap.put(data.getOrder().getProduct().getName(), map);
333 			value = data.getOrder().getAmount();
334 		} else
335 		{
336 			Map map = (Map) this.productDayMap.get(data.getOrder().getProduct()
337 					.getName());
338 
339 			if (map.containsKey("" + this.currentDay))
340 			{
341 				value = ((Double) map.get("" + this.currentDay)).doubleValue();
342 				value += data.getOrder().getAmount();
343 				map.put("" + this.currentDay, new Double(value));
344 			} else
345 			{
346 				value = data.getOrder().getAmount();
347 				map.put("" + this.currentDay, new Double(value));
348 			}
349 		}
350 
351 		// lookup table row
352 		try
353 		{
354 			int rowNumber = 0;
355 			try
356 			{
357 				rowNumber = ((Integer) GlobalRowOrColumnNumber
358 						.getDayNumberTableRowNumber().get(new Integer(day)))
359 						.intValue();
360 			} catch (NullPointerException nullPointerException)
361 			{
362 				if (this.dateWithinRange(data))
363 				{
364 					Logger.severe(this, "handlePerDay",
365 							"Date should be in range, but no row found.");
366 				} else
367 				{
368 					// set the scope outside the game
369 					rowNumber = GlobalRowOrColumnNumber.getDayHeaderValues()
370 							.size() + 10;
371 				}
372 			}
373 
374 			Event event = new Event(
375 					ConfirmedProductionHandler.EVENT_NUMBER_ORDERPRODUCED_DAY,
376 					this, new ProducedOrderDayData(data.getOrder().getProduct()
377 							.getName(), rowNumber, value));
378 			this.fireEvent(event);
379 			this.addToHistory("day", day, data.getOrder().getProduct()
380 					.getName(), event);
381 		} catch (Exception exception)
382 		{
383 			Logger.severe(this, "handlePerDay", exception);
384 		}
385 	}
386 
387 	/***
388 	 * handles an orderconfirmation per week
389 	 * 
390 	 * @param data the data
391 	 * @param calendar the calendar
392 	 */
393 	private void handlePerWeek(final ProductionStartedData data,
394 			final Calendar calendar)
395 	{
396 		int week = (calendar.get(Calendar.WEEK_OF_YEAR));
397 
398 		// we compensate extra years
399 		week += this.additionalWeeks;
400 
401 		if (this.currentWeek != week)
402 		{
403 			this.currentWeek = week;
404 		}
405 
406 		double value = Double.NaN;
407 		if (!this.productWeekMap.containsKey(data.getOrder().getProduct()
408 				.getName()))
409 		{
410 			Map map = new HashMap();
411 			map.put("" + this.currentWeek, new Double(data.getOrder()
412 					.getAmount()));
413 			this.productWeekMap
414 					.put(data.getOrder().getProduct().getName(), map);
415 			value = data.getOrder().getAmount();
416 		} else
417 		{
418 			Map map = (Map) this.productWeekMap.get(data.getOrder()
419 					.getProduct().getName());
420 
421 			if (map.containsKey("" + this.currentWeek))
422 			{
423 				value = ((Double) map.get("" + this.currentWeek)).doubleValue();
424 				value += data.getOrder().getAmount();
425 				map.put("" + this.currentWeek, new Double(value));
426 			} else
427 			{
428 				value = data.getOrder().getAmount();
429 				map.put("" + this.currentWeek, new Double(value));
430 			}
431 		}
432 
433 		// lookup table row
434 		try
435 		{
436 			int rowNumber = 0;
437 			try
438 			{
439 				rowNumber = ((Integer) GlobalRowOrColumnNumber
440 						.getWeekNumberTableRowNumber().get(new Integer(week)))
441 						.intValue();
442 			} catch (NullPointerException nullPointerException)
443 			{
444 				if (this.dateWithinRange(data))
445 				{
446 					Logger.severe(this, "handlePerWeek",
447 							"Date should be in range, but no row found.");
448 				} else
449 				{
450 					// set the scope outside the game
451 					rowNumber = GlobalRowOrColumnNumber.getWeekHeaderValues()
452 							.size() + 10;
453 				}
454 			}
455 			Event event = new Event(
456 					ConfirmedProductionHandler.EVENT_NUMBER_ORDERPRODUCED_WEEK,
457 					this, new ProducedOrderWeekData(data.getOrder()
458 							.getProduct().getName(), rowNumber, value));
459 			this.fireEvent(event);
460 			this.addToHistory("week", week, data.getOrder().getProduct()
461 					.getName(), event);
462 		} catch (Exception exception)
463 		{
464 			Logger.severe(this, "handlePerWeek", exception);
465 		}
466 	}
467 
468 	/***
469 	 * handles an orderconfirmation per month
470 	 * 
471 	 * @param data the data
472 	 * @param calendar the calendar
473 	 */
474 	private void handlePerMonth(final ProductionStartedData data,
475 			final Calendar calendar)
476 	{
477 		int month = calendar.get(Calendar.MONTH);
478 
479 		// we compensate extra years
480 		month += this.additionalMonths;
481 
482 		if (this.currentMonth != month)
483 		{
484 			this.currentMonth = month;
485 		}
486 
487 		double value = Double.NaN;
488 		if (!this.productMonthMap.containsKey(data.getOrder().getProduct()
489 				.getName()))
490 		{
491 			Map map = new HashMap();
492 			map.put("" + this.currentMonth, new Double(data.getOrder()
493 					.getAmount()));
494 			this.productMonthMap.put(data.getOrder().getProduct().getName(),
495 					map);
496 			value = data.getOrder().getAmount();
497 		} else
498 		{
499 			Map map = (Map) this.productMonthMap.get(data.getOrder()
500 					.getProduct().getName());
501 
502 			if (map.containsKey("" + this.currentMonth))
503 			{
504 				value = ((Double) map.get("" + this.currentMonth))
505 						.doubleValue();
506 				value += data.getOrder().getAmount();
507 				map.put("" + this.currentMonth, new Double(value));
508 			} else
509 			{
510 				value = data.getOrder().getAmount();
511 				map.put("" + this.currentMonth, new Double(value));
512 			}
513 		}
514 
515 		// lookup table row
516 		try
517 		{
518 			int rowNumber = 0;
519 			try
520 			{
521 				rowNumber = ((Integer) GlobalRowOrColumnNumber
522 						.getMonthNumberTableRowNumber().get(new Integer(month)))
523 						.intValue();
524 			} catch (NullPointerException nullPointerException)
525 			{
526 				if (this.dateWithinRange(data))
527 				{
528 					Logger.severe(this, "handlePerMonth",
529 							"Date should be in range, but no row found.");
530 				} else
531 				{
532 					// set the scope outside the game
533 					rowNumber = GlobalRowOrColumnNumber.getMonthHeaderValues()
534 							.size() + 10;
535 				}
536 			}
537 			Event event = new Event(
538 					ConfirmedProductionHandler.EVENT_NUMBER_ORDERPRODUCED_MONTH,
539 					this, new ProducedOrderMonthData(data.getOrder()
540 							.getProduct().getName(), rowNumber, value));
541 			this.fireEvent(event);
542 			this.addToHistory("month", month, data.getOrder().getProduct()
543 					.getName(), event);
544 		} catch (Exception exception)
545 		{
546 			Logger.severe(this, "handlePerMonth", exception);
547 		}
548 	}
549 
550 	/***
551 	 * @see org.gscg.common.interactionlayer.AnnounceInterface#announce(nl.tudelft.simulation.event.EventType,
552 	 *      boolean)
553 	 */
554 	public void announce(final EventType eventType, final boolean announce)
555 	{
556 		if (announce)
557 		{
558 			for (Iterator i = this.history.keySet().iterator(); i.hasNext();)
559 			{
560 				HashMap dateMap = (HashMap) this.history.get(i.next());
561 				for (Iterator ii = dateMap.keySet().iterator(); ii.hasNext();)
562 				{
563 					HashMap productMap = (HashMap) dateMap.get(ii.next());
564 					for (Iterator iii = productMap.keySet().iterator(); iii
565 							.hasNext();)
566 					{
567 						try
568 						{
569 							this.owner
570 									.notifyAnnounced((EventInterface) productMap
571 											.get(iii.next()));
572 						} catch (RemoteException remoteException)
573 						{
574 							Logger.severe(this, "announce", remoteException);
575 						}
576 
577 					}
578 				}
579 			}
580 		}
581 	}
582 
583 	/***
584 	 * Method addToHistory.
585 	 * 
586 	 * @param dateType a string reflecting the date type, e.g. day, week or
587 	 *        month
588 	 * @param key the key, i.e. the number of the day, week or month
589 	 * @param productName the name of the product the confirmed order is
590 	 *        received for
591 	 * @param event the event to store in the history
592 	 */
593 	private void addToHistory(final String dateType, final int key,
594 			final String productName, final Event event)
595 	{
596 		HashMap dateMap = (HashMap) this.history.get(dateType);
597 		if (dateMap == null)
598 		{
599 			dateMap = new HashMap();
600 			this.history.put(dateType, dateMap);
601 		}
602 
603 		HashMap productMap = (HashMap) dateMap.get(productName);
604 		if (productMap == null)
605 		{
606 			productMap = new HashMap();
607 			dateMap.put(productName, productMap);
608 		}
609 		productMap.put(new Integer(key), event);
610 	}
611 
612 	/***
613 	 * calculates whether a date is within range
614 	 * 
615 	 * @param data the data
616 	 * @return returns false if the date is not within range, true otherwise
617 	 */
618 	private boolean dateWithinRange(final ProductionStartedData data)
619 	{
620 		Calendar startCalendar = Calendar.getInstance();
621 		startCalendar.setTimeInMillis(GlobalRowOrColumnNumber.getStartTime());
622 
623 		Calendar endCalendar = Calendar.getInstance();
624 		DateIntData date = null;
625 		try
626 		{
627 			date = DateIntData.makeDateIntData(data.getEndDate(), this.owner
628 					.getOwner().getDEVSSimulator());
629 		} catch (RemoteException remoteException)
630 		{
631 			Logger.severe(this, "dateWithinRange", remoteException);
632 		}
633 		endCalendar.set(date.getYear(), date.getMonth(), date.getDay());
634 
635 		long runLength = GlobalRowOrColumnNumber.getNumberOfDays()
636 				* TimeUnitInterface.DAY.getValue();
637 		if ((endCalendar.getTimeInMillis() - startCalendar.getTimeInMillis()) > runLength)
638 		{
639 			return false;
640 		}
641 		return true;
642 
643 	}
644 }