private TChunk getSingleChunk <TChunk, TPacketChunk>(TPacketChunk packet, ushort chunkId, string packetType, string chunkType, bool isAllowMultiple = false, bool isMandatory = true) where TChunk : PsnChunk where TPacketChunk : PsnPacketChunk { if (packet.RawSubChunks.Count(c => c.RawChunkId == chunkId) > 1) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(packet, isAllowMultiple, $"{packetType} packet contains multiple {chunkType} chunks")); if (!isAllowMultiple) { return(null); } } var chunk = (TChunk)packet.RawSubChunks.FirstOrDefault(c => c.RawChunkId == chunkId); if (chunk == null) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(packet, !isMandatory, $"{packetType} missing {chunkType} chunk")); return(null); } return(chunk); }
/// <summary> /// Override to provide new behavior for receipt of a PosiStageNet data packet /// </summary> protected virtual void OnDataPacketReceived(PsnDataPacketChunk dataPacket) { if (dataPacket.SubChunks.Count(c => c.ChunkId == PsnDataPacketChunkId.PsnDataHeader) > 1) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(dataPacket, false, "Packet contains multiple data packet header chunks")); return; } var headerChunk = (PsnDataHeaderChunk) dataPacket.SubChunks.FirstOrDefault(c => c.ChunkId == PsnDataPacketChunkId.PsnDataHeader); if (headerChunk == null) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(dataPacket, false, "Packet missing data packet header chunk")); return; } if (_currentDataPacketChunks.Any()) { if (headerChunk.TimeStamp != _lastDataPacketHeader.TimeStamp || headerChunk.FramePacketCount != _lastDataPacketHeader.FramePacketCount) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(_currentDataPacketChunks, false, "Incomplete packet chunk discarded, did not receive all packets for data frame")); _currentDataPacketChunks.Clear(); } } _lastDataPacketHeader = headerChunk; _currentDataPacketChunks.Add(dataPacket); if (_currentDataPacketChunks.Count == _lastDataPacketHeader.FramePacketCount) { OnCompleteDataFrameReceived(_lastDataPacketHeader, _currentDataPacketChunks); _currentDataPacketChunks.Clear(); } }
/// <summary> /// Override to provide new behavior for receipt of a PosiStageNet info packet /// </summary> protected virtual void OnInfoPacketReceived(PsnInfoPacketChunk infoPacket) { var headerChunk = getSingleChunk <PsnInfoHeaderChunk, PsnInfoPacketChunk>(infoPacket, (ushort)PsnInfoPacketChunkId.PsnInfoHeader, "Info", "info header"); if (headerChunk == null) { return; } var systemNameChunk = getSingleChunk <PsnInfoSystemNameChunk, PsnInfoPacketChunk>(infoPacket, (ushort)PsnInfoPacketChunkId.PsnInfoSystemName, "Info", "system name"); if (systemNameChunk == null) { return; } if (_currentInfoPacketChunks.Any()) { if (headerChunk.TimeStamp != _lastInfoPacketHeader.TimeStamp || headerChunk.FramePacketCount != _lastInfoPacketHeader.FramePacketCount) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(_currentInfoPacketChunks, false, "Incomplete packet chunk discarded, did not receive all packets for info frame")); _currentInfoPacketChunks.Clear(); } } _lastInfoPacketHeader = headerChunk; _currentInfoPacketChunks.Add(infoPacket); if (_currentInfoPacketChunks.Count == _lastInfoPacketHeader.FramePacketCount) { OnCompleteInfoFrameReceived(_lastInfoPacketHeader, systemNameChunk.SystemName, _currentInfoPacketChunks); _currentInfoPacketChunks.Clear(); } }
/// <summary> /// Override to provide new behavior for a receipt of a complete PosiStageNet data frame /// </summary> /// <param name="header"></param> /// <param name="dataPackets"></param> protected virtual void OnCompleteDataFrameReceived(PsnDataHeaderChunk header, IReadOnlyCollection <PsnDataPacketChunk> dataPackets) { var dataTrackerChunks = dataPackets.SelectMany(p => ((PsnDataTrackerListChunk)p.SubChunks.Single(c => c.ChunkId == PsnDataPacketChunkId.PsnDataTrackerList)) .SubChunks) .ToList(); if (dataTrackerChunks.Select(c => c.TrackerId).Distinct().Count() != dataTrackerChunks.Count) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(dataPackets, false, "Duplicate tracker IDs in frame")); return; } foreach (var chunk in dataTrackerChunks) { if (!_trackers.TryGetValue(chunk.TrackerId, out var tracker)) { tracker = new PsnTracker(chunk.TrackerId); } Tuple <float, float, float> position = null; Tuple <float, float, float> speed = null; Tuple <float, float, float> orientation = null; Tuple <float, float, float> acceleration = null; Tuple <float, float, float> targetPosition = null; float?validity = null; foreach (var subchunk in chunk.SubChunks) { switch (subchunk.ChunkId) { case PsnDataTrackerChunkId.PsnDataTrackerPos: position = ((PsnDataTrackerPosChunk)subchunk).Vector; break; case PsnDataTrackerChunkId.PsnDataTrackerSpeed: speed = ((PsnDataTrackerSpeedChunk)subchunk).Vector; break; case PsnDataTrackerChunkId.PsnDataTrackerOri: orientation = ((PsnDataTrackerOriChunk)subchunk).Vector; break; case PsnDataTrackerChunkId.PsnDataTrackerStatus: validity = ((PsnDataTrackerStatusChunk)subchunk).Validity; break; case PsnDataTrackerChunkId.PsnDataTrackerAccel: acceleration = ((PsnDataTrackerAccelChunk)subchunk).Vector; break; case PsnDataTrackerChunkId.PsnDataTrackerTrgtPos: targetPosition = ((PsnDataTrackerTrgtPosChunk)subchunk).Vector; break; default: throw new ArgumentOutOfRangeException(nameof(subchunk.ChunkId)); } } tracker = PsnTracker.CloneInternal(tracker, dataTimeStamp: header.TimeStamp, position: position, clearPosition: position == null, speed: speed, clearSpeed: speed == null, orientation: orientation, clearOrientation: orientation == null, acceleration: acceleration, clearAcceleration: acceleration == null, targetposition: targetPosition, clearTargetPosition: targetPosition == null, validity: validity, clearValidity: validity == null); _trackers[chunk.TrackerId] = tracker; } TrackersUpdated?.Invoke(this, Trackers); }
/// <summary> /// Override to provide new behavior for receipt of a complete PosiStageNet info frame /// </summary> /// <param name="header"></param> /// <param name="systemName"></param> /// <param name="infoPackets"></param> protected virtual void OnCompleteInfoFrameReceived(PsnInfoHeaderChunk header, string systemName, IReadOnlyCollection <PsnInfoPacketChunk> infoPackets) { var infoTrackerChunks = infoPackets.SelectMany(p => ((PsnInfoTrackerListChunk)p.SubChunks.Single(c => c.ChunkId == PsnInfoPacketChunkId.PsnInfoTrackerList)) .SubChunks) .ToList(); if (infoTrackerChunks.Select(c => c.TrackerId).Distinct().Count() != infoTrackerChunks.Count) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(infoPackets, false, "Duplicate tracker IDs in frame")); return; } if (RemoteSystemName != systemName) { RemoteSystemName = systemName; RemoteSystemNameUpdated?.Invoke(this, RemoteSystemName); } foreach (var chunk in infoTrackerChunks) { PsnInfoTrackerNameChunk trackerNameChunk = null; foreach (var subchunk in chunk.SubChunks) { switch (subchunk.ChunkId) { case PsnInfoTrackerChunkId.PsnInfoTrackerName: if (trackerNameChunk != null) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(infoPackets, !IsStrict, $"Tracker ID {chunk.TrackerId} has multiple tracker name chunks")); if (IsStrict) { return; } } trackerNameChunk = (PsnInfoTrackerNameChunk)subchunk; break; default: throw new ArgumentOutOfRangeException(); } } if (trackerNameChunk == null) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(infoPackets, !IsStrict, $"Tracker ID {chunk.TrackerId} has no tracker name chunk")); if (IsStrict) { return; } continue; } if (!_trackers.TryGetValue(chunk.TrackerId, out var tracker)) { tracker = new PsnTracker(chunk.TrackerId, trackerNameChunk.TrackerName, null, header.TimeStamp); _trackers.TryAdd(chunk.TrackerId, tracker); } else { tracker = tracker.SetTrackerName(trackerNameChunk.TrackerName); tracker = tracker.SetInfoTimeStamp(header.TimeStamp); } _trackers[chunk.TrackerId] = tracker; } TrackersUpdated?.Invoke(this, Trackers); }
/// <summary> /// Override to provide new behavior for receipt of a complete PosiStageNet info frame /// </summary> /// <param name="header"></param> /// <param name="systemName"></param> /// <param name="infoPackets"></param> protected virtual void OnCompleteInfoFrameReceived(IPEndPoint ipEndPoint, PsnInfoHeaderChunk header, string systemName, IReadOnlyCollection <PsnInfoPacketChunk> infoPackets) { var infoTrackerChunks = infoPackets.SelectMany(p => ((PsnInfoTrackerListChunk)p.SubChunks.Single(c => c.ChunkId == PsnInfoPacketChunkId.PsnInfoTrackerList)) .SubChunks) .ToList(); if (infoTrackerChunks.Select(c => c.TrackerId).Distinct().Count() != infoTrackerChunks.Count) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(infoPackets, false, "Duplicate tracker IDs in frame")); return; } var endPointData = getEndpointData(ipEndPoint); if (endPointData.RemoteSystemName != systemName) { string lastSystemName = systemName; endPointData.UpdateRemoteSystemName(systemName); RemoteSystemNameUpdated?.Invoke(this, new Tuple <string, EndPointData>(lastSystemName, endPointData)); } foreach (var chunk in infoTrackerChunks) { PsnInfoTrackerNameChunk trackerNameChunk = null; foreach (var subChunk in chunk.SubChunks) { switch (subChunk.ChunkId) { case PsnInfoTrackerChunkId.PsnInfoTrackerName: if (trackerNameChunk != null) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(infoPackets, !IsStrict, $"Tracker ID {chunk.TrackerId} has multiple tracker name chunks")); if (IsStrict) { return; } } trackerNameChunk = (PsnInfoTrackerNameChunk)subChunk; break; default: throw new ArgumentOutOfRangeException(); } } if (trackerNameChunk == null) { InvalidPacketReceived?.Invoke(this, new InvalidPacketsReceivedEventArgs(infoPackets, !IsStrict, $"Tracker ID {chunk.TrackerId} has no tracker name chunk")); if (IsStrict) { return; } continue; } if (!_endpoints.TryGetValue(ipEndPoint, out var trackerList)) { trackerList.updateTracker(chunk, trackerNameChunk, header); _endpoints.TryAdd(ipEndPoint, trackerList); } else { trackerList.updateTracker(chunk, trackerNameChunk, header); } TrackersUpdated?.Invoke(this, new TrackerUpdateEventArgs() { TrackerId = chunk.TrackerId, Data = endPointData }); } }