/// <summary> /// Constructs a Points. /// </summary> /// <param name="schema">The schema object containing data about these points.</param> /// <param name="id">The Run8 internal ID number of these points.</param> /// <param name="subArea">The sub-area that contains these points.</param> /// <param name="world">The containing world.</param> public Points(Schema.Points schema, ushort id, SubArea subArea, World world) { Debug.Assert(id >= 0); Debug.Assert(world != null); SubArea = subArea.ID; ID = id; World = world; OccupiedImpl = true; ProvedImpl = false; { TrackCircuit[] protectingTCs = new TrackCircuit[schema.ProtectingTCs.Count]; for (int i = 0; i != schema.ProtectingTCs.Count; ++i) { protectingTCs[i] = subArea.TrackCircuits[schema.ProtectingTCs[i]]; protectingTCs[i].PropertyChanged += OnTCPropChanged; } ProtectingTCs = Array.AsReadOnly(protectingTCs); } }
/// <summary> /// Accepts a packet from Run8. /// </summary> /// <param name="data">The data packet.</param> public void UpdateFromRun8(TrainData data) { // Sanity check. Debug.Assert(data.TrainID == ID); // Timestamp the new arrival. DataLastUpdated = DateTime.UtcNow; // Update general properties. Company = data.RailroadInitials; LocoNumber = data.LocoNumber; Tag = data.TrainSymbol; Speed = (int)data.TrainSpeedMph; SpeedLimit = data.TrainSpeedLimitMPH; EngineerType = data.EngineerType; EngineerName = (EngineerType == EEngineerType.AI) ? "AI" : (EngineerType == EEngineerType.None) ? "No Driver" : data.EngineerName; Length = data.TrainLengthFeet; Weight = data.TrainWeightTons; HPt = data.HpPerTon; // Update AI hold/relinquish orders. We don't use the property setters here because the property setters send order packets to Run8, as they are intended for invocation by the signaller. Instead set the backing fields emit property change notifications directly. SetProperty(ref AIHoldImpl, data.HoldingForDispatcher, nameof(AIHold)); SetProperty(ref AIRelinquishImpl, data.RelinquishWhenStopped, nameof(AIRelinquish)); // Update location, keeping the old string if not available. SubArea sub = null; TrackCircuit tc = null; if (data.BlockID >= 0) { ushort subAreaID, tcID; if (data.BlockID >= 100000) { subAreaID = (ushort)(data.BlockID / 1000); tcID = (ushort)(data.BlockID % 1000); } else if (data.BlockID >= 10000) { subAreaID = (ushort)(data.BlockID / 100); tcID = (ushort)(data.BlockID % 100); } else { subAreaID = (ushort)(data.BlockID / 10); tcID = (ushort)(data.BlockID % 10); } if (World.Region.SubAreas.TryGetValue(subAreaID, out sub)) { if (tcID < sub.TrackCircuits.Count) { tc = sub.TrackCircuits[tcID]; } } } if (tc != null) { SubArea = sub.Name; Location = tc.LocationName; } LocationCurrent = tc != null; // Fix up which track circuit we are in. TrackCircuit berth = tc?.GetBerth(); if (berth != TrackCircuit) { if (TrackCircuit != null) { TrackCircuit.Trains.Remove(this); } TrackCircuit = berth; if (TrackCircuit != null) { TrackCircuit.Trains.Add(this); } } }