private void OnAnimationRequestReceived(byte[] message) { // The frame might be split up into multiple packages. // We keep a buffer (FrameProtocol for each frame) to wait for all packages. // When the frame is complete, we call FrameReceived. int frameIndex = FrameProtocol.GetFrameIndex(message); FrameWithoutDelta frame = null; lock (_resourceLock) { if (_frameSectionBuffer == null) { _frameSectionBuffer = new Dictionary <int, FrameProtocol>(); } if (!_frameSectionBuffer.ContainsKey(frameIndex)) { _frameSectionBuffer.Add(frameIndex, new FrameProtocol()); } if (_frameSectionBuffer[frameIndex].TryDeserialize(message, out frame)) { _frameSectionBuffer.Remove(frameIndex); } } if (frame != null) { OnFrameReceived(frame); } }
public void TryDeserialize_frameWithThreeFrameSetsOutOfOrder_DeserializesCorrectly() { int maxPackageSize = 1016; int timeUnitsPerFrame = 100; int frameIndex = 9; FrameWithoutDelta expectedFrame = new FrameWithoutDelta(frameIndex, timeUnitsPerFrame, 900); for (int i = 0; i < 900; i++) { expectedFrame[i] = new PixelInstruction(1, 2, 3); } // RUN byte[][] packages = FrameProtocol.SerializeFrame(expectedFrame, maxPackageSize); Assert.AreEqual(3, packages.Length); FrameProtocol protocol = new FrameProtocol(); FrameWithoutDelta frame; Assert.AreEqual(false, protocol.TryDeserialize(packages[0], out frame)); Assert.AreEqual(false, protocol.TryDeserialize(packages[2], out frame)); Assert.AreEqual(true, protocol.TryDeserialize(packages[1], out frame)); Assert.IsNotNull(frame); Assert.AreEqual(expectedFrame.Index, frame.Index); Assert.AreEqual(expectedFrame.TimeStampRelative, frame.TimeStampRelative); Assert.AreEqual(expectedFrame.Count, frame.Count); CollectionAssert.AreEquivalent(expectedFrame.Items, frame.Items); }
public void SerializeFrame_FrameTooBigForASinglePackage_CreatesCorrectByteArray() { int maxPackageSize = 1016; int timeUnitsPerFrame = 100; int frameIndex = 9; FrameWithoutDelta frame = new FrameWithoutDelta(frameIndex, timeUnitsPerFrame, 900); for (int i = 0; i < 900; i++) { frame[i] = new PixelInstruction(1, 2, 3); } // RUN byte[][] packages = FrameProtocol.SerializeFrame(frame, maxPackageSize); // Deserialize FrameProtocol frameProtocol = new FrameProtocol(); FrameWithoutDelta deserializedFrame = null; foreach (byte[] package in packages) { if (frameProtocol.TryDeserialize(package, out FrameWithoutDelta returnFrame)) { deserializedFrame = returnFrame; } } // Assert Assert.AreEqual(3, packages.Length); Assert.IsNotNull(deserializedFrame); CollectionAssert.AreEqual(frame.Items, deserializedFrame.Items); }
private void OnFrameReceived(FrameWithoutDelta frame) { EventHandler <FrameWithoutDelta> handler = RenderFrameReceived; if (handler != null) { handler(this, frame); } }
public static byte[][] SerializeFrame(FrameWithoutDelta frame, int maxSizePerPackage) { int bytesNeeded = HEADER_BYTES_NEEDED + frame.Items.Length * PixelInstructionProtocol.BYTES_NEEDED; byte[][] packages; if (bytesNeeded <= maxSizePerPackage) { // The frame is small enough to fit in a single packet. // No FrameSectionPackage is needed. packages = new byte[1][]; packages[0] = new byte[bytesNeeded]; BitConverter.GetBytes(frame.Index).CopyTo(packages[0], 0); // Sequence index BitConverter.GetBytes(frame.TimeStampRelative).CopyTo(packages[0], 4); // TimeStamp (relative) BitConverter.GetBytes(frame.Count).CopyTo(packages[0], 8); // Number of PixelInstructions BitConverter.GetBytes(false).CopyTo(packages[0], 12); // Has FrameSections for (int i = 0; i < frame.Count; i++) { int bufferStartIndex = HEADER_BYTES_NEEDED + i * PixelInstructionProtocol.BYTES_NEEDED; PixelInstructionProtocol.Serialize(frame[i], packages[0], bufferStartIndex); } return(packages); } // This frame is too big to send as one packet. // Create multiple packages with the help of FrameSectionPackage // First, calculate the inital package int headerBytesNeeded = HEADER_BYTES_NEEDED + FrameSectionProtocol.HEADER_BYTES_NEEDED; int instructionsInFirstSection = (maxSizePerPackage - headerBytesNeeded) / PixelInstructionProtocol.BYTES_NEEDED; // Second, calculate how many FrameSections are needed int instructionsThatFitInOtherSections = (maxSizePerPackage - FrameSectionProtocol.HEADER_BYTES_NEEDED) / PixelInstructionProtocol.BYTES_NEEDED; int frameSectionsNeeded = (int)Math.Ceiling((double)(frame.Count - instructionsInFirstSection) / instructionsThatFitInOtherSections) + 1; // +1 for the first section packages = new byte[frameSectionsNeeded][]; // Third, create the Frame header and its first section packages[0] = new byte[headerBytesNeeded + instructionsInFirstSection * PixelInstructionProtocol.BYTES_NEEDED]; BitConverter.GetBytes(frame.Index).CopyTo(packages[0], 0); // Sequence index BitConverter.GetBytes(frame.TimeStampRelative).CopyTo(packages[0], 4); // TimeStamp (relative) BitConverter.GetBytes(frameSectionsNeeded).CopyTo(packages[0], 8); // Number of FrameSets BitConverter.GetBytes(true).CopyTo(packages[0], 12); // Has FrameSections CreateFrameSection(packages[0], 13, frame, frame.Index, 0, 0, instructionsInFirstSection); // Lastely, create the other frame sections. A new byte array (package) for each FrameSection. for (int i = 0; i < frameSectionsNeeded - 1; i++) { int instructionStartIndex = instructionsInFirstSection + i * instructionsThatFitInOtherSections; int instructionsInThisSection = Math.Min(instructionsThatFitInOtherSections, frame.Count - instructionStartIndex); packages[i + 1] = new byte[FrameSectionProtocol.HEADER_BYTES_NEEDED + PixelInstructionProtocol.BYTES_NEEDED * instructionsInThisSection]; CreateFrameSection(packages[i + 1], 0, frame, frame.Index, i + 1, instructionStartIndex, instructionsInThisSection); } return(packages); }
private static void CreateFrameSection(byte[] buffer, int bufferStartIndex, FrameWithoutDelta frame, int frameIndex, int sectionIndex, int instructionStartIndex, int numberOfInstructions) { FrameSectionPackage package = new FrameSectionPackage(); package.FrameSequenceIndex = frameIndex; package.Index = sectionIndex; package.pixelInstructions = new List <PixelInstruction>(); for (int i = instructionStartIndex; i < instructionStartIndex + numberOfInstructions; i++) { package.pixelInstructions.Add(frame[i]); } FrameSectionProtocol.Serialize(package, buffer, bufferStartIndex); }
public void Render__LedStripRenderGetsCalled() { FrameWithoutDelta frame = new FrameWithoutDelta(0, 100, 1); frame[0] = new PixelInstruction(10, 20, 30); var mock = new Mock <ILEDStrip>(); mock.Setup(x => x.Render()); LedController controller = new LedController(mock.Object); controller.RenderFrame(frame); mock.Verify(x => x.Render(), Times.Once); }
public void GetFrameIndex_FrameAndFrameSection_GetsCorrectIndex() { int maxPackageSize = 1016; int timeUnitsPerFrame = 100; int frameIndex = 9; FrameWithoutDelta expectedFrame = new FrameWithoutDelta(frameIndex, timeUnitsPerFrame, 900); for (int i = 0; i < 900; i++) { expectedFrame[i] = new PixelInstruction(1, 2, 3); } // RUN byte[][] packages = FrameProtocol.SerializeFrame(expectedFrame, maxPackageSize); Assert.AreEqual(3, packages.Length); Assert.AreEqual(frameIndex, FrameProtocol.GetFrameIndex(packages[0])); Assert.AreEqual(frameIndex, FrameProtocol.GetFrameIndex(packages[1])); Assert.AreEqual(frameIndex, FrameProtocol.GetFrameIndex(packages[2])); }
public void RenderFrame(FrameWithoutDelta frame) { if (Monitor.TryEnter(lockObject)) { try { for (int i = 0; i < frame.Count; i++) { PixelInstruction instruction = frame[i]; _ledStrip.SetLEDColor(0, i, System.Drawing.Color.FromArgb(instruction.R, instruction.G, instruction.B)); } _ledStrip.Render(); } finally { Monitor.Exit(lockObject); } } }
public void TryDeserialize_frameWithNoFrameSets_DeserializesCorrectly() { int timeUnitsPerFrame = 100; FrameWithoutDelta expectedFrame = new FrameWithoutDelta(1, timeUnitsPerFrame, 3); expectedFrame[0] = new PixelInstruction(1, 2, 3); expectedFrame[1] = new PixelInstruction(4, 5, 6); expectedFrame[2] = new PixelInstruction(7, 8, 9); byte[] bytes = new byte[sizeof(int) + sizeof(int) + sizeof(int) + sizeof(bool) + PixelInstructionProtocol.BYTES_NEEDED * 3]; int startIndex = 0; BitConverter.GetBytes(1).CopyTo(bytes, startIndex); // Frame index BitConverter.GetBytes(timeUnitsPerFrame).CopyTo(bytes, startIndex += 4); // Timestamp (relative) BitConverter.GetBytes(expectedFrame.Count).CopyTo(bytes, startIndex += 4); // Number of PixelInstructions BitConverter.GetBytes(false).CopyTo(bytes, startIndex += 4); // Has FrameSections // instruction 1 bytes[startIndex += 1] = 1; bytes[startIndex += 1] = 2; bytes[startIndex += 1] = 3; // instruction 2 bytes[startIndex += 1] = 4; bytes[startIndex += 1] = 5; bytes[startIndex += 1] = 6; // instruction 3 bytes[startIndex += 1] = 7; bytes[startIndex += 1] = 8; bytes[startIndex += 1] = 9; FrameProtocol protocol = new FrameProtocol(); FrameWithoutDelta frame; Assert.AreEqual(true, protocol.TryDeserialize(bytes, out frame)); Assert.IsNotNull(frame); Assert.AreEqual(expectedFrame.Index, frame.Index); Assert.AreEqual(expectedFrame.TimeStampRelative, frame.TimeStampRelative); Assert.AreEqual(expectedFrame.Count, frame.Count); CollectionAssert.AreEqual(expectedFrame.Items, frame.Items); }
public void PrepareFrame_Frame_FrameGetsDrawnToStrip() { FrameWithoutDelta frame = new FrameWithoutDelta(0, 100, 1); frame[0] = new PixelInstruction(10, 20, 30); var mock = new Mock <ILEDStrip>(); int index = -1; Color color = new Color(); mock.Setup(x => x.SetLEDColor(It.IsAny <int>(), It.IsAny <int>(), It.IsAny <Color>())).Callback <int, int, Color>((i, j, c) => { index = j; color = c; }); LedController controller = new LedController(mock.Object); controller.RenderFrame(frame); Assert.AreEqual(0, index); Assert.AreEqual(Color.FromArgb(10, 20, 30), color); }
public void SerializeFrame_Frame_CreatesCorrectByteArray() { int maxPackageSize = 1016; int timeUnitsPerFrame = 100; int frameIndex = 9; FrameWithoutDelta frame = new FrameWithoutDelta(frameIndex, timeUnitsPerFrame, 3); frame[0] = new PixelInstruction(1, 2, 3); frame[1] = new PixelInstruction(4, 5, 6); frame[2] = new PixelInstruction(7, 8, 9); byte[] expectedBytes = new byte[sizeof(int) + sizeof(int) + sizeof(int) + sizeof(bool) + 3 + 3 + 3]; int startIndex = 0; BitConverter.GetBytes(frameIndex).CopyTo(expectedBytes, startIndex); // Frame index BitConverter.GetBytes(timeUnitsPerFrame).CopyTo(expectedBytes, startIndex += 4); BitConverter.GetBytes(frame.Count).CopyTo(expectedBytes, startIndex += 4); // Number of PixelInstructions BitConverter.GetBytes(false).CopyTo(expectedBytes, startIndex += 4); // Has FrameSections // instruction 1 expectedBytes[startIndex += 1] = 1; expectedBytes[startIndex += 1] = 2; expectedBytes[startIndex += 1] = 3; // instruction 2 expectedBytes[startIndex += 1] = 4; expectedBytes[startIndex += 1] = 5; expectedBytes[startIndex += 1] = 6; // instruction 3 expectedBytes[startIndex += 1] = 7; expectedBytes[startIndex += 1] = 8; expectedBytes[startIndex += 1] = 9; byte[][] packages = FrameProtocol.SerializeFrame(frame, maxPackageSize); Assert.AreEqual(1, packages.Length); Assert.AreEqual(expectedBytes, packages[0]); }
/// <summary> /// Deserialize a package /// </summary> /// <param name="buffer"></param> /// <returns>True if the frame has been deserialized, False if not all packages have been received.</returns> public bool TryDeserialize(byte[] bytes, out FrameWithoutDelta frame) { frame = null; if (_frameSectionPackages == null) { // First package. // Read Frame header int startIndex = 0; _frameIndex = BitConverter.ToInt32(bytes, startIndex); _timeStampRelative = BitConverter.ToInt32(bytes, startIndex += 4); int itemCount = BitConverter.ToInt32(bytes, startIndex += 4); bool hasFrameSections = BitConverter.ToBoolean(bytes, startIndex += 4); startIndex += 1; if (hasFrameSections) { _frameSectionPackages = new FrameSectionPackage[itemCount]; _frameSectionsReceived = new bool[itemCount]; _frameSectionsReceived[0] = true; // The rest of the package is a FrameSection _frameSectionPackages[0] = FrameSectionProtocol.Deserialize(bytes, startIndex); return(false); } else { frame = new FrameWithoutDelta(_frameIndex, _timeStampRelative, itemCount); // The rest of the package contains PixelInstructions for (int i = 0; i < itemCount; i++) { int instructionStartIndex = startIndex + i * PixelInstructionProtocol.BYTES_NEEDED; frame[i] = PixelInstructionProtocol.Deserialize(bytes, instructionStartIndex); } return(true); } } else { // Subsequent package. Always starts with a FrameSection. FrameSectionPackage package = FrameSectionProtocol.Deserialize(bytes, 0); if (package.FrameSequenceIndex != _frameIndex) { throw new System.Net.ProtocolViolationException( $"The frameIndex of the frameSection does not match with the Frame's index. Expected :{_frameIndex}, Received: {package.FrameSequenceIndex}"); } _frameSectionPackages[package.Index] = package; _frameSectionsReceived[package.Index] = true; if (_frameSectionsReceived.All(x => x)) { // TODO add totalNumberOfPixelInstructions to frame header int totalPixelInstructions = _frameSectionPackages.Sum(x => x.NumberOfPixelInstructions); frame = new FrameWithoutDelta(_frameIndex, _timeStampRelative, totalPixelInstructions); int index = 0; for (int i = 0; i < _frameSectionPackages.Length; i++) { for (int j = 0; j < _frameSectionPackages[i].NumberOfPixelInstructions; j++) { frame[index++] = _frameSectionPackages[i].pixelInstructions[j]; } } return(true); } return(false); } }