private bool Expected(string message) { string loweredMessage = message.ToLowerInvariant(); if (loweredMessage.StartsWith("unexpected ")) { throw new InvalidOperationException(string.Format("Table Manager tells me I screwed up: '{0}'", message)); } if (loweredMessage.StartsWith("disconnect")) { this.state = TableManagerProtocolState.WaitForDisconnect; return(true); } for (int a = 0; a < this.tableManagerExpectedResponse.Length; a++) { if (loweredMessage.StartsWith(this.tableManagerExpectedResponse[a].ToLowerInvariant())) { return(true); } } if (message.StartsWith("Teams")) { return(true); // bug in BridgeMoniteur } Log.Trace(0, "Unexpected response by {3}: '{0}' in state {2}; expected '{1}'", message, this.tableManagerExpectedResponse[0], this.state, this.seat); throw new InvalidOperationException(string.Format("Unexpected response by {2} '{0}'; expected '{1}'", message, this.tableManagerExpectedResponse[0], this.seat)); }
public override void HandleTournamentStopped() { base.HandleTournamentStopped(); this.moreBoards = false; this.state = TableManagerProtocolState.Finished; this.HandleSessionEnd(); this.Stop(); }
private void ChangeState(TableManagerProtocolState newState, bool waitForSync, bool waitForMoreStateChanges, string[] expectedAnswers, string message, params object[] args) { message = string.Format(message, args); var stateChange = new StateChange() { Message = message, NewState = newState, ExpectedResponses = expectedAnswers, WaitForSync = waitForSync, WaitForStateChanges = waitForMoreStateChanges }; lock (this.stateChanges) this.stateChanges.Enqueue(stateChange); #if syncTrace //Log.Trace("Client {0} queued state change {1} ({2} states on the q)", this.seat, newState, this.stateChanges.Count); #endif }
private async Task ProcessStateChanges() { const int minimumWait = 0; var waitForNewMessage = minimumWait; do { waitForNewMessage = 5; while (this.stateChanges.Count > 0 && !this.WaitForProtocolSync) { waitForNewMessage = minimumWait; StateChange stateChange; lock (this.stateChanges) stateChange = this.stateChanges.Dequeue(); //if (this.state != stateChange.NewState) { this.state = stateChange.NewState; #if syncTrace Log.Trace(2, "Client {0} new state {1} message='{2}' expects='{3}'", this.seat, this.state, stateChange.Message, stateChange.ExpectedResponses[0]); #endif } this.tableManagerExpectedResponse = stateChange.ExpectedResponses; if (stateChange.Message.Length > 0) { await this.WriteProtocolMessageToRemoteMachine(stateChange.Message); } this.WaitForProtocolSync = stateChange.WaitForSync; // e.g. must wait for 'to lead' message this.WaitForBridgeEvents = stateChange.WaitForStateChanges; } if (waitForNewMessage > minimumWait) { await Task.Delay(waitForNewMessage); } } while (this.moreBoards); }
private void ProcessMessage(string message) { #if syncTrace Log.Trace(2, "Client {1} processing '{0}'", message, seat); #endif if (message == "End of session" || message.StartsWith("NS:") // something new from Bridge Moniteur: session ends with ) { this.EventBus.HandleTournamentStopped(); return; } if (Expected(message)) { try { switch (this.state) { case TableManagerProtocolState.WaitForSeated: this.HandleSeated(); this.ChangeState(TableManagerProtocolState.WaitForTeams, false, false, new string[] { "Teams" }, "{0} ready for teams", this.seat); break; case TableManagerProtocolState.WaitForTeams: this.teamNS = message.Substring(message.IndexOf("N/S : \"") + 7); this.teamNS = teamNS.Substring(0, teamNS.IndexOf("\"")); this.teamEW = message.Substring(message.IndexOf("E/W : \"") + 7); this.teamEW = teamEW.Substring(0, teamEW.IndexOf("\"")); if (this.team != (this.seat.IsSameDirection(Seats.North) ? this.teamNS : this.teamEW)) { throw new ArgumentOutOfRangeException("team", "Seated in another team"); } this.HandleTeams(teamNS, teamEW); this.ChangeState(TableManagerProtocolState.WaitForStartOfBoard, false, false, new string[] { "Start of board", "End of session" }, "{0} ready to start", this.seat); break; case TableManagerProtocolState.WaitForStartOfBoard: if (message.StartsWith("Teams")) { // bug in BridgeMoniteur when tournament is restarted } else if (message.StartsWith("Timing")) { // Timing - N/S : this board [minutes:seconds], total [hours:minutes:seconds]. E/W : this board [minutes:seconds], total [hours:minutes:seconds]". // Bridge Moniteur does not send the '.' at the end of the message // Timing - N/S : this board 01:36, total 0:01:36. E/W : this board 01:34, total 0:01:34 if (!message.EndsWith(".")) { message += "."; } string[] timing = message.Split('.'); string[] parts = timing[0].Split(','); string boardNS = "00:" + parts[0].Substring(parts[0].IndexOf("board") + 6).Trim(); string totalNS = parts[1].Substring(parts[1].IndexOf("total") + 6).Trim(); parts = timing[1].Split(','); string boardEW = "00:" + parts[0].Substring(parts[0].IndexOf("board") + 6).Trim(); string totalEW = parts[1].Substring(parts[1].IndexOf("total") + 6).Trim(); TimeSpan _boardNS = ParseTimeUsed(boardNS); TimeSpan _totalNS = ParseTimeUsed(totalNS); TimeSpan _boardEW = ParseTimeUsed(boardEW); TimeSpan _totalEW = ParseTimeUsed(totalEW); this.EventBus.HandleTimeUsed(_boardNS, _totalNS, _boardEW, _totalEW); } else { this.ChangeState(TableManagerProtocolState.WaitForBoardInfo, false, false, new string[] { "Board number" }, "{0} ready for deal", this.seat); } break; case TableManagerProtocolState.WaitForBoardInfo: // "Board number 1. Dealer North. Neither vulnerable." string[] dealInfoParts = message.Split('.'); int boardNumber = Convert.ToInt32(dealInfoParts[0].Substring(13)); this.theDealer = SeatsExtensions.FromXML(dealInfoParts[1].Substring(8)); Vulnerable vulnerability = Vulnerable.Neither; switch (dealInfoParts[2].Substring(1)) { case "Both vulnerable": vulnerability = Vulnerable.Both; break; case "N/S vulnerable": vulnerability = Vulnerable.NS; break; case "E/W vulnerable": vulnerability = Vulnerable.EW; break; } var board = new Board2(this.theDealer, vulnerability, new Distribution()); this.CurrentResult = new TMBoardResult(this, board, new SeatCollection <string>(new string[] { this.teamNS, this.teamEW, this.teamNS, this.teamEW })); this.EventBus.HandleBoardStarted(boardNumber, this.theDealer, vulnerability); this.ChangeState(TableManagerProtocolState.WaitForMyCards, false, false, new string[] { this.seat + "'s cards : " }, "{0} ready for cards", this.seat); break; case TableManagerProtocolState.WaitForMyCards: // "North's cards : S J 8 5.H A K T 8.D 7 6.C A K T 3." // "North's cards : S J 8 5.H A K T 8.D.C A K T 7 6 3." // "North's cards : S -.H A K T 8 4 3 2.D.C A K T 7 6 3." string cardInfo = message.Substring(2 + message.IndexOf(":")); string[] suitInfo = cardInfo.Split('.'); for (int s1 = 0; s1 < 4; s1++) { suitInfo[s1] = suitInfo[s1].Trim(); Suits s = SuitHelper.FromXML(suitInfo[s1].Substring(0, 1)); if (suitInfo[s1].Length > 2) { string cardsInSuit = suitInfo[s1].Substring(2) + " "; if (cardsInSuit.Substring(0, 1) != "-") { while (cardsInSuit.Length > 1) { Ranks rank = Rank.From(cardsInSuit.Substring(0, 1)); this.EventBus.HandleCardPosition(this.seat, s, rank); cardsInSuit = cardsInSuit.Substring(2); } } } } //this.EventBus.WaitForEventCompletion(); // TM is now expecting a response: either a bid or a 'ready for bid' this.EventBus.HandleCardDealingEnded(); break; case TableManagerProtocolState.WaitForOtherBid: if (message.StartsWith("Explain ")) { message = message.Substring(8); string[] answer = message.Split(' '); Seats bidder = SeatsExtensions.FromXML(answer[0]); var bid = new Bid(answer[answer.Length - 1], ""); this.EventBus.HandleExplanationNeeded(bidder, bid); } else { this.WaitForBridgeEvents = true; ProtocolHelper.HandleProtocolBid(message, this.EventBus); } break; case TableManagerProtocolState.WaitForDummiesCards: //Log.Trace("Client {1} processing dummies cards", message, seat); string dummiesCards = message.Substring(2 + message.IndexOf(":")); string[] suitInfo2 = dummiesCards.Split('.'); for (Suits s = Suits.Spades; s >= Suits.Clubs; s--) { int suit = 3 - (int)s; suitInfo2[suit] = suitInfo2[suit].Trim(); if (suitInfo2[suit].Length > 2) { string cardsInSuit = suitInfo2[suit].Substring(2) + " "; if (cardsInSuit.Substring(0, 1) != "-") { while (cardsInSuit.Length > 1) { Ranks rank = Rank.From(cardsInSuit.Substring(0, 1)); this.EventBus.HandleCardPosition(this.CurrentResult.Play.Dummy, s, rank); cardsInSuit = cardsInSuit.Substring(2); } } } } this.WaitForProtocolSync = false; this.EventBus.HandleShowDummy(this.CurrentResult.Play.Dummy); break; case TableManagerProtocolState.WaitForLead: this.WaitForProtocolSync = false; break; case TableManagerProtocolState.WaitForCardPlay: if (message.Contains("to lead")) { /// This indicates a timing issue: TM sent a '... to lead' message before TD sent its HandleTrickFinished event /// Wait until I receveive the HandleTrickFinished event Log.Trace(1, "TableManagerClient.ProcessMessage {0}: received 'to lead' before HandleTrickFinished", this.seat); //Debugger.Break(); } else { string[] cardPlay = message.Split(' '); Seats player = SeatsExtensions.FromXML(cardPlay[0]); Card card = new Card(SuitHelper.FromXML(cardPlay[2].Substring(1, 1)), Rank.From(cardPlay[2].Substring(0, 1))); if (player != this.CurrentResult.Play.Dummy) { this.EventBus.HandleCardPosition(player, card.Suit, card.Rank); } this.WaitForBridgeEvents = true; this.EventBus.HandleCardPlayed(player, card.Suit, card.Rank); } break; case TableManagerProtocolState.WaitForDisconnect: this.state = TableManagerProtocolState.Finished; this.EventBus.HandleTournamentStopped(); break; } } catch (Exception ex) { Log.Trace(0, "Error while processing message '{0}' in state {1}: {2}", message, state, ex.ToString()); throw; } } else { // unexpected message } }
private void ChangeState(string message, string expected, TableManagerProtocolState newState, Seats seat) { var exp = expected.Split(';'); if (message.ToLowerInvariant().Replace(" ", " ").StartsWith(exp[0].ToLowerInvariant()) || (exp.Length >= 2 && message.ToLowerInvariant().Replace(" ", " ").StartsWith(exp[1].ToLowerInvariant()))) { this.clients[seat].state = newState; var allReady = true; for (Seats s = Seats.North; s <= Seats.West; s++) { if (this.clients[s] == null || this.clients[s].state != newState) { allReady = false; break; } } if (allReady) { #if syncTrace Log.Trace(2, "{1} ChangeState {0}", newState, this.Name); #endif switch (newState) { case TableManagerProtocolState.Initial: break; case TableManagerProtocolState.WaitForSeated: break; case TableManagerProtocolState.WaitForTeams: this.BroadCast("Teams : N/S : \"" + this.clients[Seats.North].teamName + "\" E/W : \"" + this.clients[Seats.East].teamName + "\""); this.OnHostEvent(this, HostEvents.ReadyForTeams, null); break; case TableManagerProtocolState.WaitForStartOfBoard: this.c.StartNextBoard().Wait(); break; case TableManagerProtocolState.WaitForBoardInfo: this.BroadCast("Board number {0}. Dealer {1}. {2} vulnerable.", this.c.currentBoard.BoardNumber, this.c.currentBoard.Dealer.ToXMLFull(), ProtocolHelper.Translate(this.c.currentBoard.Vulnerable)); break; case TableManagerProtocolState.WaitForMyCards: this.clients[Seats.North].WriteData(ProtocolHelper.Translate(Seats.North, this.c.currentBoard.Distribution)); this.clients[Seats.East].WriteData(ProtocolHelper.Translate(Seats.East, this.c.currentBoard.Distribution)); this.clients[Seats.South].WriteData(ProtocolHelper.Translate(Seats.South, this.c.currentBoard.Distribution)); this.clients[Seats.West].WriteData(ProtocolHelper.Translate(Seats.West, this.c.currentBoard.Distribution)); break; case TableManagerProtocolState.WaitForCardPlay: break; case TableManagerProtocolState.WaitForOtherBid: for (Seats s = Seats.North; s <= Seats.West; s++) { this.clients[s].state = TableManagerProtocolState.WaitForCardPlay; } ProtocolHelper.HandleProtocolBid(this.lastRelevantMessage, this.EventBus); break; case TableManagerProtocolState.WaitForOtherCardPlay: ProtocolHelper.HandleProtocolPlay(this.lastRelevantMessage, this.EventBus); break; case TableManagerProtocolState.WaitForOwnCardPlay: break; case TableManagerProtocolState.WaitForDummiesCardPlay: break; case TableManagerProtocolState.GiveDummiesCards: var cards = ProtocolHelper.Translate(this.CurrentResult.Play.Dummy, this.c.currentBoard.Distribution).Replace(this.CurrentResult.Play.Dummy.ToXMLFull(), "Dummy"); for (Seats s = Seats.North; s <= Seats.West; s++) { if (s != this.CurrentResult.Play.Dummy) { this.clients[s].WriteData(cards); } } for (Seats s = Seats.North; s <= Seats.West; s++) { this.clients[s].state = (s == this.CurrentResult.Auction.Declarer ? TableManagerProtocolState.WaitForOwnCardPlay : TableManagerProtocolState.WaitForCardPlay); lock (this.clients) this.clients[s].Pause = false; } break; case TableManagerProtocolState.WaitForDisconnect: break; case TableManagerProtocolState.WaitForLead: break; case TableManagerProtocolState.Finished: break; default: break; } } } else { #if syncTrace Log.Trace(0, "{1} expected '{0}'", expected, this.Name); this.DumpQueue(); #endif this.clients[seat].Refuse("Expected '{0}'", expected); throw new InvalidOperationException(string.Format("Expected '{0}'", expected)); } }