/*
 * JaamSim Discrete Event Simulation
 * Copyright (C) 2013 Ausenco Engineering Canada Inc.
 * Copyright (C) 2018-2020 JaamSim Software Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jaamsim.CalculationObjects;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import com.jaamsim.Graphics.DisplayEntity;
import com.jaamsim.basicsim.Entity;
import com.jaamsim.basicsim.EntityTarget;
import com.jaamsim.events.ProcessTarget;
import com.jaamsim.input.Keyword;
import com.jaamsim.input.ValueInput;
import com.jaamsim.units.TimeUnit;

/**
 * Generates update signals that are sent to the objects managed by the Controller.
 * @author Harry King
 *
 */
public class Controller extends DisplayEntity {

	@Keyword(description = "Simulation time for the first update signal.",
	         exampleList = {"5 s"})
	private final ValueInput firstTime;

	@Keyword(description = "Time interval between update signals.",
	         exampleList = {"100 ms"})
	private final ValueInput interval;

	private final ArrayList<Controllable> entityList;  // Entities controlled by this Controller.
	private int count;  // Number of update cycle completed.

	private final ProcessTarget doUpdate = new DoUpdateTarget(this);

	{
		firstTime = new ValueInput("FirstTime", KEY_INPUTS, 0.0d);
		firstTime.setUnitType(TimeUnit.class);
		firstTime.setValidRange(0.0, Double.POSITIVE_INFINITY);
		this.addInput(firstTime);

		interval = new ValueInput("Interval", KEY_INPUTS, 1.0d);
		interval.setUnitType(TimeUnit.class);
		interval.setValidRange(0.0, Double.POSITIVE_INFINITY);
		this.addInput(interval);
		this.addSynonym(interval, "SamplingTime");
	}

	public Controller() {
		entityList = new ArrayList<>();
	}

	@Override
	public void earlyInit() {
		super.earlyInit();
		count = 0;

		// Prepare a list of the calculation entities managed by this controller
		entityList.clear();
		for (Entity ent : getJaamSimModel().getClonesOfIterator(Entity.class, Controllable.class)) {
			Controllable con = (Controllable) ent;
			if (con.getController() == this)
				entityList.add(con);
		}

		// Sort the calculation entities into the correct sequence
		Collections.sort(entityList, new Comparator<Controllable>() {

			@Override
			public int compare(Controllable c1, Controllable c2) {
				return Double.compare(c1.getSequenceNumber(), c2.getSequenceNumber());
			}

		});
	}

	@Override
	public void startUp() {
		super.startUp();

		// Schedule the first update
		this.scheduleProcess(firstTime.getValue(), 5, doUpdate);
	}

	private static class DoUpdateTarget extends EntityTarget<Controller> {
		DoUpdateTarget(Controller ent) {
			super(ent, "doUpdate");
		}

		@Override
		public void process() {
			ent.doUpdate();
		}
	}

	public void doUpdate() {

		// Update the last value for each entity
		double simTime = this.getSimTime();
		for (Controllable ent : entityList) {
			ent.update(simTime);
		}

		// Increment the number of cycles
		count++;

		// Schedule the next update
		this.scheduleProcess(interval.getValue(), 5, doUpdate);
	}

	public int getCount() {
		return count;
	}

}

@@ -981,32 +981,71 @@
 .ProcessTarget;%0A
+import com.jaamsim.input.IntegerInput;%0A
 import com.jaams
@@ -1062,16 +1062,16 @@
 eyword;%0A
-
 import c
@@ -1582,16 +1582,153 @@
 erval;%0A%0A
+%09@Keyword(description = %22Maximum number of updates to perform.%22,%0A%09         exampleList = %7B%225%22%7D)%0A%09private final IntegerInput maxUpdates;%0A%0A
 %09private
@@ -2359,16 +2359,174 @@
 gTime%22);
+%0A%0A%09%09maxUpdates = new IntegerInput(%22MaxUpdates%22, KEY_INPUTS, Integer.MAX_VALUE);%0A%09%09maxUpdates.setValidRange(0, Integer.MAX_VALUE);%0A%09%09this.addInput(maxUpdates);
 %0A%09%7D%0A%0A%09pu
@@ -3323,32 +3323,66 @@
 he first update%0A
+%09%09if (maxUpdates.getValue() %3E 0)%0A%09
 %09%09this.scheduleP
@@ -3420,24 +3420,24 @@
  doUpdate);%0A
-
 %09%7D%0A%0A%09private
@@ -3887,16 +3887,54 @@
  update%0A
+%09%09if (count %3C maxUpdates.getValue())%0A%09
 %09%09this.s

add keyword 'MaxUpdates' to Controller