/// <summary> /// Constructs a DssSimulationMgr object. /// </summary> /// <param name="root">The root object.</param> public DssSimulationMgr(DssRoot root) { if (root == null) { throw new ArgumentNullException("root"); } this.initialized = false; this.hostLeft = false; this.root = root; this.aftCalculator = null; this.aptCalculators = null; this.currentRound = null; this.nextRound = null; this.nextNextRound = null; this.nextNextNextRound = null; this.waitingForCommit = false; this.commitTimeoutClock = null; this.simulationFrameClock = null; this.commitAwMonitors = null; this.commitAwTimeouts = null; this.operatorFlags = null; }
/// <see cref="IDisposable.Dispose"/> public void Dispose() { Dispose_i(); this.simulationMgr.Dispose(); this.alarmClockMgr.Dispose(); this.eventQueue.Dispose(); instance = null; }
/// <summary> /// Call this function if a list of commands has been arrived from the given operator in the given frame. /// </summary> /// <param name="theCommands">List of the arrived commands.</param> /// <param name="opIdx">The index of the sender operator.</param> /// <param name="frameIdx">The index of the frame of the commands.</param> /// <returns>True in case of success, false otherwise.</returns> public bool Command(RCPackage[] theCommands, int opIdx, int frameIdx) { if (theCommands == null || theCommands.Length == 0) { throw new ArgumentNullException("theCommands"); } if (opIdx < 0 || opIdx >= this.commitFlags.Length) { throw new ArgumentOutOfRangeException("opIdx"); } if (frameIdx < 0 || frameIdx >= this.simulatorCommands.Length) { throw new ArgumentOutOfRangeException("frameNum"); } for (int i = 0; i < theCommands.Length; i++) { if (theCommands[i] == null || !theCommands[i].IsCommitted || theCommands[i].PackageType != RCPackageType.CUSTOM_DATA_PACKAGE || DssRoot.IsInternalFormat(theCommands[i].PackageFormat)) { return(false); } } if (this.initialized) { if (!this.commitFlags[opIdx]) { if (this.simulatorCommands[frameIdx][opIdx] == null) { this.simulatorCommands[frameIdx][opIdx] = theCommands; return(true); } else { /// Command list has been already sent by the given operator for the given frame. return(false); } } else { /// The round has been already committed by the sender operator --> error. return(false); } } else { throw new DssException("Uninitialized SimulationRound!"); } }
/// <summary> /// Creates an answer to the current setup step at guest side. /// </summary> /// <param name="outgoingPackages">The outgoing packages sent by the client module.</param> /// <returns>List of the packages of the outgoing setup step answer.</returns> /// <remarks>This function must be called at guest side.</remarks> public RCPackage[] CreateAnswer(RCPackage[] outgoingPackages) { if (this.mode != DssMode.GUEST_SIDE) { throw new DssException("This call is only allowed at guest side!"); } if (this.state != SetupStepState.READY) { throw new DssException("You can only create answer in READY state!"); } if (outgoingPackages == null) { throw new ArgumentNullException("outgoingPackages"); } int numPackages = outgoingPackages.Length + 2; /// BEGIN, END RCPackage[] retList = new RCPackage[numPackages]; for (int i = 0; i < numPackages; i++) { if (i == 0) /// BEGIN { retList[i] = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_SETUP_STEP_AW_BEGIN); retList[i].WriteInt(0, this.stepID); } else if (i == numPackages - 1) /// END { retList[i] = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_SETUP_STEP_MSG_END); retList[i].WriteInt(0, this.stepID); } else /// Package from the client module { if (outgoingPackages[i - 1] != null && outgoingPackages[i - 1].IsCommitted && outgoingPackages[i - 1].PackageType == RCPackageType.NETWORK_CONTROL_PACKAGE && !DssRoot.IsInternalFormat(outgoingPackages[i - 1].PackageFormat)) { retList[i] = outgoingPackages[i - 1]; } else { throw new DssException("Outgoing setup package format error!"); } } } /// end-for return(retList); }
/// <summary> /// Internal function to timestamp and send the given commands to the lobby. /// </summary> /// <param name="cmds">The list of the commands to send.</param> private void SendCommandsToLobby(RCPackage[] cmds) { if (!this.initialized) { throw new DssException("DssSimulationMgr is uninitialized!"); } if (cmds != null && cmds.Length != 0) { /// Compute the length of the buffer needed for sending the commands as a byte sequence int bufferLength = 0; for (int i = 0; i < cmds.Length; i++) { if (cmds[i] != null && cmds[i].IsCommitted && cmds[i].PackageType == RCPackageType.CUSTOM_DATA_PACKAGE && !DssRoot.IsInternalFormat(cmds[i].PackageFormat)) { bufferLength += cmds[i].PackageLength; } else { throw new DssException("Unexpected command from the client module!"); } } /// Create the buffer and write the commands into this buffer byte[] cmdBuffer = new byte[bufferLength]; int offset = 0; for (int i = 0; i < cmds.Length; i++) { offset += cmds[i].WritePackageToBuffer(cmdBuffer, offset); } /// Create the DSS_COMMAND package and send it to the lobby RCPackage cmdPackage = RCPackage.CreateNetworkCustomPackage(DssRoot.DSS_COMMAND); cmdPackage.WriteInt(0, this.nextNextRound.RoundIndex); /// Round index of the command cmdPackage.WriteInt(1, this.currentRound.CurrentFrameIndex); /// Frame index of the command cmdPackage.WriteByteArray(2, cmdBuffer); /// The command list this.root.LobbyIface.SendPackage(cmdPackage); /// Send the command package to the lobby } }
/// <summary> /// Constructs a DssRoot object. /// </summary> /// <param name="simulatorIface">Interface of the simulator in the client module.</param> /// <exception cref="DssException">If another instance of this class exists.</exception> public DssRoot(ISimulator simulatorIface) { if (instance != null) { throw new DssException("Another instance of DssRoot already exists!"); } this.simulatorIface = simulatorIface; this.eventQueue = new DssEventQueue(); this.alarmClockMgr = new AlarmClockManager(this.eventQueue); this.idOfThisPeer = -1; this.opCount = -1; this.lobbyIface = null; this.simulationMgr = new DssSimulationMgr(this); this.lifeTimer = new Stopwatch(); this.lifeTimer.Start(); instance = this; TraceManager.WriteAllTrace("DssRoot successfully created.", DssTraceFilters.SETUP_STAGE_INFO); }
/// <summary> /// Constructs a DssEventHandler object. /// </summary> public DssEventHandler(DssRoot root) { this.root = root; }
/// <summary> /// Called when a control packages arrived from the host during setup stage. /// </summary> /// <param name="package">The arrived control package.</param> public void SetupStageCtrlPackage(RCPackage package) { if (this.sm.CurrentState == this.WaitingConnectionACK) { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_CONN_ACK) { int otherMajor = package.ReadInt(0); int otherMinor = package.ReadInt(1); int otherBuild = package.ReadInt(2); int otherRevision = package.ReadInt(3); if (otherMajor >= 0 && otherMinor >= 0 && otherBuild >= 0 && otherRevision >= 0) { Version otherVer = new Version(otherMajor, otherMinor, otherBuild, otherRevision); if (DssRoot.IsCompatibleVersion(otherVer)) { this.WaitingConnectionACK_WaitingSetupStepRQ.Fire(); this.controller.ExecuteFirings(); return; } else { TraceManager.WriteAllTrace(string.Format("Incompatible with host version: {0} (RC.DssServices)", otherVer), DssTraceFilters.SETUP_STAGE_ERROR); SetupStageError(); return; } } else { TraceManager.WriteAllTrace("Unable to parse version information!", DssTraceFilters.SETUP_STAGE_ERROR); SetupStageError(); return; } } /// end-if (package.PackageFormat.ID == DssRoot.DSS_CTRL_CONN_ACK) else if (package.PackageFormat.ID == DssRoot.DSS_CTRL_CONN_REJECT) { TraceManager.WriteAllTrace(string.Format("Connection request rejected by the host! Reason: {0}", package.ReadString(0)), DssTraceFilters.SETUP_STAGE_ERROR); SetupStageError(); return; } else { TraceManager.WriteAllTrace("Unexpected answer from host to connection request!", DssTraceFilters.SETUP_STAGE_ERROR); SetupStageError(); return; } } else if (this.sm.CurrentState == this.WaitingSetupStepRQ) { if (package.PackageFormat.ID == DssRoot.DSS_LEAVE) { /// The host left the DSS this.guestRoot.SetupIface.HostLeftDss(); StopTimeouts(); this.guestRoot.EventQueue.ExitEventLoop(); return; } /// end-if (package.PackageFormat.ID == DssRoot.DSS_LEAVE) else if (package.PackageFormat.ID == DssRoot.DSS_CTRL_DROP_GUEST) { /// The current guest has been dropped out of the DSS by the host. this.guestRoot.SetupIface.DroppedByHost(); StopTimeouts(); this.guestRoot.EventQueue.ExitEventLoop(); return; } /// end-if (package.PackageFormat.ID == DssRoot.DSS_CTRL_DROP_GUEST) else if (package.PackageFormat.ID == DssRoot.DSS_CTRL_START_SIMULATION) { int[] leftList = null; int[] lostList = null; bool[] opFlags = null; if (ParseStartSimPackage(package, out leftList, out lostList, out opFlags)) { StopTimeouts(); CallNotificationMethods(leftList, lostList); this.guestRoot.SetupIface.SimulationStarted(); this.opFlagsTmp = opFlags; this.WaitingSetupStepRQ_Simulating.Fire(); this.controller.ExecuteFirings(); return; } else { SetupStageError(); return; } } /// end-if (package.PackageFormat.ID == DssRoot.DSS_CTRL_START_SIMULATION) else { this.guestRoot.Step.IncomingPackage(package); if (this.guestRoot.Step.State == SetupStepState.READY) { /// Setup step request arrived. this.WaitingSetupStepRQ_SendingSetupStepAW.Fire(); this.controller.ExecuteFirings(); return; } else if (this.guestRoot.Step.State == SetupStepState.ERROR) { /// Setup step request error. SetupStageError(); return; } else { /// Setup step request not finished yet, more packages to wait. return; } } } /// end-if (this.sm.CurrentState == this.WaitingSetupStepRQ) else { /// Go to error state if the package cannot be handled until now. SetupStageError(); return; } }
/// <summary> /// This function is called when a control package arrives at setup stage. /// </summary> /// <param name="package">The arrived control package.</param> public void SetupStageCtrlPackage(RCPackage package) { bool error = false; if (this.sm.CurrentState == this.WaitingConnectionRQ) { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_CONN_REQUEST) { RCPackage rejPackage = null; int otherMajor = package.ReadInt(0); int otherMinor = package.ReadInt(1); int otherBuild = package.ReadInt(2); int otherRevision = package.ReadInt(3); if (otherMajor >= 0 && otherMinor >= 0 && otherBuild >= 0 && otherRevision >= 0) { Version otherVer = new Version(otherMajor, otherMinor, otherBuild, otherRevision); if (DssRoot.IsCompatibleVersion(otherVer)) { /// We send back a DSS_CTRL_CONN_ACK package. RCPackage ackPackage = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_CONN_ACK); ackPackage.WriteInt(0, DssRoot.APPLICATION_VERSION.Major); ackPackage.WriteInt(1, DssRoot.APPLICATION_VERSION.Minor); ackPackage.WriteInt(2, DssRoot.APPLICATION_VERSION.Build); ackPackage.WriteInt(3, DssRoot.APPLICATION_VERSION.Revision); this.manager.HostRoot.Lobby.SendControlPackage(ackPackage, this.channel.Index + 1); this.WaitingConnectionRQ_SendingSetupStepRQ.Fire(); } else { /// We send back a DSS_CTRL_CONN_REJECT package rejPackage = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_CONN_REJECT); string reason = string.Format("Incompatible with host version: {0} (RC.DssServices)", DssRoot.APPLICATION_VERSION.ToString()); rejPackage.WriteString(0, reason); rejPackage.WriteByteArray(1, new byte[0]); } } else { /// We create a DSS_CTRL_CONN_REJECT package rejPackage = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_CONN_REJECT); rejPackage.WriteString(0, "Unable to parse version information!"); rejPackage.WriteByteArray(1, new byte[0]); } /// We send back a DSS_CTRL_CONN_REJECT package if necessary. if (rejPackage != null && rejPackage.IsCommitted) { this.manager.HostRoot.Lobby.SendControlPackage(rejPackage, this.channel.Index + 1); error = true; } } /// end-if (package.PackageFormat.ID == DssRoot.DSS_CTRL_CONN_REQUEST) } /// end-if (this.sm.CurrentState == this.WaitingConnectionRQ) else if (this.sm.CurrentState == this.WaitingSetupStepAW) { if (package.PackageFormat.ID == DssRoot.DSS_LEAVE) { string leaveReason = package.ReadString(0); string trcMsg = string.Format("Guest-{0} has left the DSS. Reason: {1}", this.channel.Index, leaveReason.Length != 0 ? leaveReason : "-"); TraceManager.WriteAllTrace(trcMsg, DssTraceFilters.SETUP_STAGE_INFO); this.WaitingSetupStepAW_Inactive.Fire(); this.channel.GuestLeaveSetupStage(); } /// end-if (package.PackageFormat.ID == DssRoot.DSS_LEAVE) else { SetupStep currentStep = this.manager.HostRoot.GetStep(this.channel.Index); currentStep.IncomingPackage(package); if (currentStep.State == SetupStepState.READY) { /// Setup step answer arrived. this.WaitingSetupStepAW_SendingSetupStepRQ.Fire(); } else if (currentStep.State == SetupStepState.ERROR) { /// Setup step answer error. error = true; } else { /// Setup step answer not finished yet, more packages to wait. } } } /// end-if (this.sm.CurrentState == this.WaitingSetupStepAW) if (error) { /// Go to error state if the package cannot be handled until now. SetupStageError(); this.channel.SetupStageError(); return; } }
/// <summary> /// Starts a new setup step at host side. /// </summary> /// <param name="outgoingPackages">The outgoing packages sent by the client module.</param> /// <param name="leftList">List of the guests that left or were dropped from the DSS.</param> /// <param name="lostList">List of the guests that the host left connection with.</param> /// <param name="channelStates">The current state of the channels.</param> /// <returns>List of the packages of the outgoing setup step request.</returns> /// <remarks>This function must be called at host side.</remarks> public RCPackage[] CreateRequest(RCPackage[] outgoingPackages, int[] leftList, int[] lostList, DssChannelState[] channelStates) { if (this.mode != DssMode.HOST_SIDE) { throw new DssException("This call is only allowed at host side!"); } if (this.state != SetupStepState.NOT_FINISHED) { throw new DssException("Invalid setup step state!"); } if (channelStates == null || channelStates.Length < 1) { throw new ArgumentNullException("channelStates"); } if (outgoingPackages == null) { throw new ArgumentNullException("outgoingPackages"); } int numPackages = outgoingPackages.Length + 2; /// BEGIN, END RCPackage[] retList = new RCPackage[numPackages]; for (int i = 0; i < numPackages; i++) { if (i == 0) /// BEGIN { retList[i] = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_SETUP_STEP_RQ_BEGIN); retList[i].WriteInt(0, this.stepID); retList[i].WriteIntArray(1, leftList != null ? leftList : new int[0] { }); retList[i].WriteIntArray(2, lostList != null ? lostList : new int[0] { }); byte[] stateBytes = new byte[channelStates.Length]; for (int j = 0; j < channelStates.Length; j++) { stateBytes[j] = (byte)channelStates[j]; } retList[i].WriteByteArray(3, stateBytes); } else if (i == numPackages - 1) /// END { retList[i] = RCPackage.CreateNetworkControlPackage(DssRoot.DSS_CTRL_SETUP_STEP_MSG_END); retList[i].WriteInt(0, this.stepID); } else /// Package from the client module { if (outgoingPackages[i - 1] != null && outgoingPackages[i - 1].IsCommitted && outgoingPackages[i - 1].PackageType == RCPackageType.NETWORK_CONTROL_PACKAGE && !DssRoot.IsInternalFormat(outgoingPackages[i - 1].PackageFormat)) { retList[i] = outgoingPackages[i - 1]; } else { throw new DssException("Outgoing setup package format error!"); } } } /// end-for return(retList); }
/// <summary> /// Call this function if a control package has arrived from the network and you are waiting for a setup step /// answer or request. This class will parse it for you automatically. /// </summary> /// <param name="package">The incoming control package.</param> public void IncomingPackage(RCPackage package) { if (package == null) { throw new ArgumentNullException("package"); } if (!package.IsCommitted) { throw new ArgumentException("Uncommitted package!", "package"); } if (package.PackageType != RCPackageType.NETWORK_CONTROL_PACKAGE) { throw new ArgumentException("Unexpected package type!", "package"); } if (this.state != SetupStepState.NOT_FINISHED) { throw new DssException("This call is only allowed in NOT_FINISHED state!"); } if (this.mode == DssMode.HOST_SIDE) { /// Host side parsing if (this.beginArrived) { if (!DssRoot.IsInternalFormat(package.PackageFormat)) { this.packageListTmp.Add(package); } else { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_SETUP_STEP_MSG_END && package.ReadInt(0) == this.stepID) { /// Finished this.packageList = this.packageListTmp.ToArray(); this.state = SetupStepState.READY; } else { /// Error this.state = SetupStepState.ERROR; } } } else /// end-if (this.beginArrived) { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_SETUP_STEP_AW_BEGIN && package.ReadInt(0) == this.stepID) { this.beginArrived = true; } else { /// Error this.state = SetupStepState.ERROR; } } } else /// end-if (this.mode == SetupStepMode.HOST_SIDE) { /// Guest side parsing if (this.beginArrived) { if (!DssRoot.IsInternalFormat(package.PackageFormat)) { this.packageListTmp.Add(package); } else { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_SETUP_STEP_MSG_END && package.ReadInt(0) == this.stepID) { /// Finished this.packageList = this.packageListTmp.ToArray(); this.state = SetupStepState.READY; } else { /// Error this.state = SetupStepState.ERROR; } } } else /// end-if (this.beginArrived) { if (package.PackageFormat.ID == DssRoot.DSS_CTRL_SETUP_STEP_RQ_BEGIN) { /// Read the step ID this.stepID = package.ReadInt(0); /// Read the left-list int[] leftList = package.ReadIntArray(1); bool err = false; for (int i = 0; i < leftList.Length; ++i) { if (leftList[i] < 0) { err = true; break; } } if (!err) { /// OK, save the list this.leftList = leftList; } else { /// Error this.state = SetupStepState.ERROR; return; } /// Read the lost-list int[] lostList = package.ReadIntArray(2); err = false; for (int i = 0; i < lostList.Length; ++i) { if (lostList[i] < 0) { err = true; break; } } if (!err) { /// OK, save the list this.lostList = lostList; } else { /// Error this.state = SetupStepState.ERROR; return; } /// Read the channel-state-list byte[] chStateBytes = package.ReadByteArray(3); if (chStateBytes.Length != 0) { this.channelStateList = new DssChannelState[chStateBytes.Length]; for (int i = 0; i < chStateBytes.Length; ++i) { if (chStateBytes[i] == (byte)DssChannelState.CHANNEL_CLOSED || chStateBytes[i] == (byte)DssChannelState.CHANNEL_OPENED || chStateBytes[i] == (byte)DssChannelState.GUEST_CONNECTED) { this.channelStateList[i] = (DssChannelState)chStateBytes[i]; } else { /// Error this.state = SetupStepState.ERROR; return; } } } /// end-if (chStateBytes != null && chStateBytes.Length != 0) else { /// Error this.state = SetupStepState.ERROR; return; } /// Everything is OK, the begin message arrived successfully. this.beginArrived = true; } else /// end-if (package.PackageFormat.ID == DssRoot.DSS_CTRL_SETUP_STEP_RQ_BEGIN) { /// Error this.state = SetupStepState.ERROR; } } } }