From ddb5b41c6dca497cde4c2b6fc7fcedb8e6fad7c5 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Mon, 18 Jun 2018 07:01:48 -0700 Subject: [PATCH 01/25] Remove old test code This method was removed so test is no longer needed --- .../Imaging/BasePrintLogoTests.cs | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs diff --git a/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs b/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs deleted file mode 100644 index 75e77c2..0000000 --- a/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs +++ /dev/null @@ -1,37 +0,0 @@ -using PTIRelianceLib.Imaging; -using Xunit; - -namespace PTIRelianceLib.Tests.Imaging -{ - using System.ComponentModel; - - public class BasePrintLogoTests - { - [Fact()] - [Category("BMP")] - public void ApplyColorInversionTest() - { - // Input are expected are provided as resources, dithered is what - // we are testing - - var input = BinaryFile.From(Properties.Resources.white_bitmap); - - var logo = new BasePrintLogo(input); - - Assert.False(logo.IsInverted); - - logo.ApplyColorInversion(); - - var inverted = logo.ImageData; - var expected = new BasePrintLogo(BinaryFile.From(Properties.Resources.black_bitmap)).ImageData; - - // White should ivnert to black - Assert.True(ImageTestHelpers.CompareCrc32(expected, inverted)); - Assert.True(logo.IsInverted); - - // Flip back to white, test that the inversion flag is cleared - logo.ApplyColorInversion(); - Assert.False(logo.IsInverted); - } - } -} \ No newline at end of file From c9c6c4ff3b66665bb6076c9de8b11b4aae037033 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Mon, 18 Jun 2018 07:11:12 -0700 Subject: [PATCH 02/25] Check correct paper enum flag for paper present --- RelianceCLI/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RelianceCLI/Program.cs b/RelianceCLI/Program.cs index 5c87828..4170d66 100644 --- a/RelianceCLI/Program.cs +++ b/RelianceCLI/Program.cs @@ -148,7 +148,7 @@ private static void Run(Options opts) { var status = printer.GetStatus(); Console.WriteLine("Printer status:\n{0}", status); - Console.WriteLine("Has Paper? :{0}", status.SensorStatus.HasFlag(SensorStatuses.Path)); + Console.WriteLine("Has Paper? :{0}", status.SensorStatus.HasFlag(SensorStatuses.Paper)); } } From c53846055fe4256a424bc9a6f6d53d03d112477e Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 19 Jun 2018 09:45:08 -0700 Subject: [PATCH 03/25] Add start of data telemetry API This includes both volatile and non-volatile telemetry data reading --- PTIRelianceLib/IO/HIDPort.cs | 1 - PTIRelianceLib/IO/StructuredReader.cs | 53 ++++++ PTIRelianceLib/Internal/FixedArray.cs | 67 ++++++++ PTIRelianceLib/Protocol/RelianceCommands.cs | 5 + PTIRelianceLib/ReliancePrinter.cs | 63 +++++++ PTIRelianceLib/Telemetry/LifetimeTelemetry.cs | 119 +++++++++++++ PTIRelianceLib/Telemetry/PowerupTelemetry.cs | 160 ++++++++++++++++++ .../Transport/PacketParserFactory.cs | 3 + 8 files changed, 470 insertions(+), 1 deletion(-) create mode 100644 PTIRelianceLib/IO/StructuredReader.cs create mode 100644 PTIRelianceLib/Internal/FixedArray.cs create mode 100644 PTIRelianceLib/Telemetry/LifetimeTelemetry.cs create mode 100644 PTIRelianceLib/Telemetry/PowerupTelemetry.cs diff --git a/PTIRelianceLib/IO/HIDPort.cs b/PTIRelianceLib/IO/HIDPort.cs index bae4319..4366e69 100644 --- a/PTIRelianceLib/IO/HIDPort.cs +++ b/PTIRelianceLib/IO/HIDPort.cs @@ -9,7 +9,6 @@ namespace PTIRelianceLib.IO { - using System.Data; using Logging; using Internal; using Transport; diff --git a/PTIRelianceLib/IO/StructuredReader.cs b/PTIRelianceLib/IO/StructuredReader.cs new file mode 100644 index 0000000..5584b8d --- /dev/null +++ b/PTIRelianceLib/IO/StructuredReader.cs @@ -0,0 +1,53 @@ +#region Header +// StructuredReader.cs +// PTIRelianceLib +// Cory Todd +// 19-06-2018 +// 8:43 AM +#endregion + +namespace PTIRelianceLib.IO +{ + using System.Collections.Generic; + using Protocol; + using Transport; + + /// + /// Reads pages from an IO device and returns result. + /// + internal class StructuredReader + { + private readonly IPort _device; + + public StructuredReader(IPort device) + { + _device = device; + } + + public IPacket Read(params byte[] preamble) + { + var buffer = new List(); + var sequenceNum = 0; + + while (true) + { + var cmd = _device.Package(preamble); + var seq = sequenceNum.ToBytesBE(); + cmd.Add(seq); + + _device.Write(cmd); + + var resp = _device.Read(50); + if (resp.GetPacketType() != PacketTypes.PositiveAck) + { + break; + } + + buffer.AddRange(resp.GetBytes()); + ++sequenceNum; + } + + return _device.Package(buffer.ToArray()); + } + } +} \ No newline at end of file diff --git a/PTIRelianceLib/Internal/FixedArray.cs b/PTIRelianceLib/Internal/FixedArray.cs new file mode 100644 index 0000000..9bfc32f --- /dev/null +++ b/PTIRelianceLib/Internal/FixedArray.cs @@ -0,0 +1,67 @@ +#region Header +// FixedArray.cs +// PTIRelianceLib +// Cory Todd +// 19-06-2018 +// 7:50 AM +#endregion + +namespace PTIRelianceLib +{ + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Linq; + + /// + /// A read-only fixed length array + /// + /// Data type to hold + public struct FixedArray + { + private readonly IList _mData; + + /// + /// Create a new fixed length array of this type + /// + /// + public FixedArray(int size) + { + Size = size; + _mData = new List(size); + } + + /// + /// Returns the fixed length of this array. This is the max size. + /// + public int Size { get; } + + /// + /// Returns the count of elements in the fixed array. + /// + public int Count => _mData.Count; + + /// + /// Puts data into this fixed length array starting from the current tail + /// of the fixed array. If the fixed array size limit has been reached, + /// no more data will be added and this method will return. + /// + /// + public void SetData(params T[] data) + { + foreach(var d in data) + { + if (_mData.Count >= Size) + { + break; + } + _mData.Add(d); + } + } + + /// + /// Returns a readonly array containg all data + /// + /// + public ImmutableArray GetData() => _mData.ToImmutableArray(); + } +} \ No newline at end of file diff --git a/PTIRelianceLib/Protocol/RelianceCommands.cs b/PTIRelianceLib/Protocol/RelianceCommands.cs index 93b7e33..fe8219a 100644 --- a/PTIRelianceLib/Protocol/RelianceCommands.cs +++ b/PTIRelianceLib/Protocol/RelianceCommands.cs @@ -263,6 +263,11 @@ internal enum RelianceCommands /// AutocutSub = 0x88, + /// + /// Subcommand for telemetry commands + /// + TelemtrySub = 0x90, + /// /// Perform general configuration /// diff --git a/PTIRelianceLib/ReliancePrinter.cs b/PTIRelianceLib/ReliancePrinter.cs index 41150b8..bcd15d6 100644 --- a/PTIRelianceLib/ReliancePrinter.cs +++ b/PTIRelianceLib/ReliancePrinter.cs @@ -23,6 +23,7 @@ namespace PTIRelianceLib using Logging; using Logo; using Protocol; + using Telemetry; using Transport; /// @@ -500,6 +501,68 @@ public ReturnCodes PrintLogo(int index) return ReturnCodes.Okay; } + /// + /// Returns the telemetry data over the lifetime of this printer + /// + /// LifetimeTelemtry data since printer left factory + public LifetimeTelemetry GetLifetimeTelemetry() + { + var tel = GetFirmwareRevision() < new Revlev("1.28") ? + // Revert to legacy mode )always NV) for older firmware + ReadTelemetry(0, 0, 0) : + // 1: read non-volatile version, from start to end + ReadTelemetry(1, 0, 0); + return (LifetimeTelemetry) tel; + } + + /// + /// Returns the telemetry data of this printer since last power up + /// + /// Requires firmware 1.28+. Older firmware will result in null result. + /// Powerup telemetry or null if read failure or unsupported firmware + public PowerupTelemetry GetPowerupTelemetry() + { + return GetFirmwareRevision() < new Revlev("1.28") ? + // Not supported on older firmware + null : + // 2: read volatile version, from start to end + ReadTelemetry(2, 0, 0); + } + + /// + /// Reads the specified telemtry data block + /// + /// Type of telemetry to request. Set 0 for legacy mode + /// Position to start reading from, 0 base + /// How many bytes to read. 0 to read all of it. + /// + protected PowerupTelemetry ReadTelemetry(byte type, ushort offset, ushort count) + { + // Build permission request + var request = _mPort.Package((byte)RelianceCommands.TelemtrySub, 0); + if (type != 0) + { + request.Add(type); + request.Add(offset.ToBytesBE()); + request.Add(count.ToBytesBE()); + } + + // Request permission + var resp = Write(request); + if (resp.GetPacketType() != PacketTypes.PositiveAck) + { + return null; + } + + // 1: read data request + var preamble = new byte[] {(byte) RelianceCommands.TelemtrySub, 1}; + + // Execute read + var reader = new StructuredReader(_mPort); + resp = reader.Read(preamble); + return PacketParserFactory.Instance.Create().Parse(resp); + } + /// /// Returns firmware application id string or empty on error /// diff --git a/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs b/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs new file mode 100644 index 0000000..f155955 --- /dev/null +++ b/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs @@ -0,0 +1,119 @@ +#region Header +// LifetimeTelemetry.cs +// PTIRelianceLib +// Cory Todd +// 19-06-2018 +// 7:42 AM +#endregion + +namespace PTIRelianceLib.Telemetry +{ + using System.IO; + using Transport; + + /// + /// + /// Telemetry data for the lifetime of the printer. + /// This data persists across flash updates and configuration changes. + /// + public class LifetimeTelemetry : PowerupTelemetry + { + /// + /// How many times the printer has powered up. + /// + public int PowerUpCount { get; set; } + + /// + /// How many times has the HID reset command been received. + /// + public int ResetCmdCount { get; set; } + + /// + public override byte[] Serialize() + { + throw new System.NotImplementedException(); + } + } + + /// + /// + /// Parser class for consumes an + /// + /// and produces a . + /// + internal class LifetimeTelemetryParser : BaseModelParser + { + public override LifetimeTelemetry Parse(IPacket packet) + { + // Needs a valid packet and at least 8 bytes to identify type + packet = CheckPacket(packet); + if (packet == null || packet.Count < 8) + { + return null; + } + + var tel = new LifetimeTelemetry(); + using (var stream = new MemoryStream(packet.GetBytes())) + using(var reader = new BinaryReader(stream)) + { + tel.StructRevision = reader.ReadInt32(); + tel.StructSize = reader.ReadInt32(); + + // Ensure there is enough data left in the stream to read + if (packet.Count - 8 < tel.StructSize) + { + return null; + } + + tel.PowerUpCount = reader.ReadInt32(); + tel.CutterCount = reader.ReadInt32(); + tel.PlatenOpenCount = reader.ReadInt32(); + tel.PaperMovedCount = reader.ReadInt32(); + tel.TicketCount = reader.ReadInt32(); + tel.TicketsRetracted = reader.ReadInt32(); + tel.TicketsEjected = reader.ReadInt32(); + tel.TicketsPulled = reader.ReadInt32(); + + // Read in and parse raw data in appropriate width + var rawData = reader.ReadBytes(PowerupTelemetry.LengthLogLength * 4); + using (var blockStream = new MemoryStream(rawData)) + using (var blockReader = new BinaryReader(blockStream)) + { + // Read in presented length log, contains 4 byte integers + tel.TicketLengthLog = new FixedArray(rawData.Length); + for (var i = 0; i < PowerupTelemetry.LengthLogLength; ++i) + { + tel.TicketLengthLog.SetData(blockReader.ReadInt32()); + } + } + + + // Read in and parse raw data in appropriate width + rawData = reader.ReadBytes(PowerupTelemetry.PresentLogLength * 4); + using (var blockStream = new MemoryStream(rawData)) + using (var blockReader = new BinaryReader(blockStream)) + { + // Read in presented length log, contains 4 byte integers + tel.TicketPresentedLog = new FixedArray(rawData.Length); + for (var i = 0; i < PowerupTelemetry.PresentLogLength; ++i) + { + tel.TicketPresentedLog.SetData(blockReader.ReadInt32()); + } + } + + tel.AvgTimePresented = reader.ReadInt32(); + tel.Button = reader.ReadInt16(); + tel.PaperOutDuringPrint = reader.ReadInt16(); + tel.PaperOut = reader.ReadInt16(); + tel.AvgCutTime = reader.ReadInt16(); + tel.ResetCmdCount = reader.ReadInt32(); + tel.JammedCount = reader.ReadInt16(); + tel.OverheatedCount = reader.ReadInt32(); + tel.CriticalErrorCount = reader.ReadInt32(); + tel.HighErrorCount = reader.ReadInt16(); + } + + return tel; + } + } +} \ No newline at end of file diff --git a/PTIRelianceLib/Telemetry/PowerupTelemetry.cs b/PTIRelianceLib/Telemetry/PowerupTelemetry.cs new file mode 100644 index 0000000..1ae18f1 --- /dev/null +++ b/PTIRelianceLib/Telemetry/PowerupTelemetry.cs @@ -0,0 +1,160 @@ +#region Header +// RELTelemetry.cs +// PTIRelianceLib +// Cory Todd +// 19-06-2018 +// 7:19 AM +#endregion + +namespace PTIRelianceLib.Telemetry +{ + using Transport; + + /// + /// + /// Contains telemtry data describing the usage and lifetime of a Reliance + /// thermal printer. The data is produced by a Reliance printer and + /// handled by this API. + /// + public class PowerupTelemetry : IParseable + { + internal const int LengthLogLength = 9; + internal const int PresentLogLength = 11; + + /// + /// The revision of this data structure. + /// + internal int StructRevision { get; set; } + + /// + /// Size in bytes of this data structure. + /// + internal int StructSize { get; set; } + + /// + /// How many cut cycles has there been. + /// + public int CutterCount { get; set; } + + /// + /// How many times has the head been opened. + /// + public int PlatenOpenCount { get; set; } + + /// + /// How much paper has been moved by the printer + /// in it's lifetime in units of steps. 1 step = 0.125mm + /// + public int PaperMovedCount { get; set; } + + /// + /// How many tickets have been printed. Regardless of size. + /// + public int TicketCount { get; set; } + + /// + /// How many tickets have been retracted. + /// + public int TicketsRetracted { get; set; } + + /// + /// How many tickets have been ejected. + /// + public int TicketsEjected { get; set; } + + /// + /// How many tickets have been pulled by the customer. + /// + public int TicketsPulled { get; set; } + + /// + /// Each index is a ticket length range. There are 9 + /// elements in this array. + /// + public FixedArray TicketLengthLog { get; set; } + + /// + /// Each index is the time a pulled ticket was sitting + /// at the bezel. Only pulled tickets are logged. There + /// are 11 elements in this array. + /// + public FixedArray TicketPresentedLog { get; set; } + + /// + /// Avg time pulled tickets are sitting at the bezel. + /// + public int AvgTimePresented { get; set; } + + /// + /// How many times the push button action was used. + /// + public short Button { get; set; } + + /// + /// How many times printer ran out of paper during a print job. + /// + public short PaperOutDuringPrint { get; set; } + + /// + /// How many times has the printer ran out of paper including during a print job. + /// + public short PaperOut { get; set; } + + /// + /// Keeps track of the avg cut time for up to the last 500 cuts. + /// + public short AvgCutTime { get; set; } + + /// + /// How many times the printer has jammed. + /// + public short JammedCount { get; set; } + + /// + /// How many times the printer has over heated. + /// + public int OverheatedCount { get; set; } + + /// + /// How many times the printer was in a critical error. + /// + public int CriticalErrorCount { get; set; } + + /// + /// How many times the printer was in a high priority error. + /// + public short HighErrorCount { get; set; } + + /// + public virtual byte[] Serialize() + { + throw new System.NotImplementedException(); + } + } + + /// + /// + /// Parser class for consumes an + /// + /// and produces a . + /// + internal class PowerupTelemetryParser : BaseModelParser + { + public override PowerupTelemetry Parse(IPacket packet) + { + var parser = new LifetimeTelemetryParser(); + var tel = parser.Parse(packet); + if(tel == null) + { + // Parse failed + return null; + } + + // These two fields don't make sense for power up context + tel.PowerUpCount = 0; + tel.ResetCmdCount = 0; + + return tel; + } + } +} \ No newline at end of file diff --git a/PTIRelianceLib/Transport/PacketParserFactory.cs b/PTIRelianceLib/Transport/PacketParserFactory.cs index 94e568b..641d875 100644 --- a/PTIRelianceLib/Transport/PacketParserFactory.cs +++ b/PTIRelianceLib/Transport/PacketParserFactory.cs @@ -11,6 +11,7 @@ namespace PTIRelianceLib.Transport using System; using System.Collections.Generic; using Configuration; + using Telemetry; /// /// Typesafe parser locator factory. These functions extended objects known to implement IParseAs. @@ -32,6 +33,8 @@ static PacketParserFactory() Instance.Register(); Instance.Register(); Instance.Register(); + Instance.Register(); + Instance.Register(); } private PacketParserFactory() { } From bf355316d1e3e317c300165ade907b26778be5c6 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 19 Jun 2018 09:45:23 -0700 Subject: [PATCH 04/25] Bug fix in HID reports lengths --- PTIRelianceLib/IO/Internal/HIDReport.cs | 3 ++- PTIRelianceLib/ReliancePrinter.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/PTIRelianceLib/IO/Internal/HIDReport.cs b/PTIRelianceLib/IO/Internal/HIDReport.cs index d902741..0c83a0c 100644 --- a/PTIRelianceLib/IO/Internal/HIDReport.cs +++ b/PTIRelianceLib/IO/Internal/HIDReport.cs @@ -62,7 +62,8 @@ public byte[] GetPayload() { // [id] [length] [... payload ...] var payload = new byte[Data[1]]; - Array.Copy(Data, 2, payload, 0, payload.Length); + var copylen = Math.Min(payload.Length, Data.Length - 2); + Array.Copy(Data, 2, payload, 0, copylen); return payload; } diff --git a/PTIRelianceLib/ReliancePrinter.cs b/PTIRelianceLib/ReliancePrinter.cs index bcd15d6..f287ebb 100644 --- a/PTIRelianceLib/ReliancePrinter.cs +++ b/PTIRelianceLib/ReliancePrinter.cs @@ -78,8 +78,8 @@ public ReliancePrinter() { VendorId = VendorId, ProductId = ProductId, - InReportLength = 34, - OutReportLength = 34, + InReportLength = 35, + OutReportLength = 35, InReportId = 2, OutReportId = 1, NativeHid = new NativeMethods() From bd4e46e19ffc2fa1f3952045472496a15fc6c0eb Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 19 Jun 2018 09:45:38 -0700 Subject: [PATCH 05/25] Fix variable name for const --- PTIRelianceLib/Status.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PTIRelianceLib/Status.cs b/PTIRelianceLib/Status.cs index 2be9ce0..8a4e7f0 100644 --- a/PTIRelianceLib/Status.cs +++ b/PTIRelianceLib/Status.cs @@ -30,7 +30,7 @@ public class Status : IParseable /// /// 12-bit ADC factor for a 3.3 volt reference /// - private const float _mAdcConstant = 0.000806f; + private const float AdcConstant = 0.000806f; /// /// ASCII string of the head input voltage. "XX.XX" (Volts) @@ -108,11 +108,11 @@ public override string ToString() sb.AppendFormat("Head Temperature: {0} °C\n", HeadTemp); sb.AppendFormat("Head Temperature: {0} °F\n", (HeadTemp * (9 / 5.0) + 32).ToString("N0")); sb.AppendFormat("Sensor Status: {0} \n", SensorStatus); - sb.AppendFormat("Presenter Raw: {0} V DC\n", PresenterRaw * _mAdcConstant); - sb.AppendFormat("Path Raw: {0} V DC\n", PathRaw * _mAdcConstant); - sb.AppendFormat("Paper Raw: {0} V DC\n", PaperRaw * _mAdcConstant); - sb.AppendFormat("Notch Raw: {0} V DC\n", NotchRaw * _mAdcConstant); - sb.AppendFormat("Arm Raw: {0} V DC\n", ArmRaw * _mAdcConstant); + sb.AppendFormat("Presenter Raw: {0} V DC\n", PresenterRaw * AdcConstant); + sb.AppendFormat("Path Raw: {0} V DC\n", PathRaw * AdcConstant); + sb.AppendFormat("Paper Raw: {0} V DC\n", PaperRaw * AdcConstant); + sb.AppendFormat("Notch Raw: {0} V DC\n", NotchRaw * AdcConstant); + sb.AppendFormat("Arm Raw: {0} V DC\n", ArmRaw * AdcConstant); sb.AppendFormat("Ticket Status: {0} \n", TicketStatus); sb.AppendFormat("Errors Status: {0} \n", PrinterErrors); From 3022a200f4da4c76fe883d95d18239ca2f2893c8 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 19 Jun 2018 12:23:49 -0700 Subject: [PATCH 06/25] Add unit test This tests FixedArray, LifetimeTelemetry, and PowerupTelemetry --- .../Internal/FixedArrayTests.cs | 42 ++++++++ .../Telemetry/LifetimeTelemetryParserTests.cs | 99 +++++++++++++++++++ .../Telemetry/PowerupTelemetryParserTests.cs | 37 +++++++ PTIRelianceLib/Internal/FixedArray.cs | 1 - PTIRelianceLib/Telemetry/LifetimeTelemetry.cs | 11 ++- .../Transport/PacketParserFactory.cs | 3 + 6 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 PTIRelianceLib.Tests/Internal/FixedArrayTests.cs create mode 100644 PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs create mode 100644 PTIRelianceLib.Tests/Telemetry/PowerupTelemetryParserTests.cs diff --git a/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs b/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs new file mode 100644 index 0000000..193b6c6 --- /dev/null +++ b/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs @@ -0,0 +1,42 @@ +using System; +using System.Linq; +using Xunit; + +namespace PTIRelianceLib.Tests.Internal +{ + public class FixedArrayTests + { + [Fact] + public void TestCtor() + { + var farr = new FixedArray(10); + Assert.Equal(10, farr.Size); + Assert.Equal(0, farr.Count); + } + + [Fact] + public void TestSetData() + { + var farr = new FixedArray(3); + + farr.SetData( + BinaryFile.From(new byte[0]), + BinaryFile.From(new byte[1]), + BinaryFile.From(new byte[2]), + BinaryFile.From(new byte[3]) // This one should be silently discarded + ); + + Assert.Equal(farr.Count, farr.Size); + + var data = farr.GetData(); + Assert.Equal(data.Length, farr.Size); + + // Test that each length is what we expect + var len = 0; + foreach (var bf in data) + { + Assert.Equal(len++, bf.Length); + } + } + } +} diff --git a/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs b/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs new file mode 100644 index 0000000..14eb2aa --- /dev/null +++ b/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs @@ -0,0 +1,99 @@ +using System; +using System.Linq; +using Xunit; + +namespace PTIRelianceLib.Tests.Telemetry +{ + using PTIRelianceLib.Telemetry; + using PTIRelianceLib.Transport; + + public class LifetimeTelemetryParserTests + { + [Fact] + public void TestIsRegistered() + { + PacketParserFactory.Instance.Create(); + } + + [Fact] + public void TestNullPacket() + { + // Test the null object is returned + var parser = PacketParserFactory.Instance.Create(); + var tel = parser.Parse(null); + Assert.Null(tel); + } + + [Fact] + public void TestShortPacket() + { + // Test the null object is returned + var parser = PacketParserFactory.Instance.Create(); + var tel = parser.Parse(new ReliancePacket(new byte[7])); + Assert.Null(tel); + } + + [Fact] + public void TestMinimumPacket() + { + // Test the null object is returned + var parser = PacketParserFactory.Instance.Create(); + var tel = parser.Parse(new ReliancePacket(new byte[8])); + Assert.Null(tel); + } + + [Fact] + public void TestZeroedPacket() + { + // Test the null object is returned when struct size field is zero + var parser = PacketParserFactory.Instance.Create(); + var tel = parser.Parse(new ReliancePacket(new byte[112])); // arbitrarily large + Assert.Null(tel); + } + + [Fact] + public void TestMaxedPacket() + { + // Test the null object is returned when struct size field is zero + var parser = PacketParserFactory.Instance.Create(); + var data = new byte[112]; // arbitrary size + Array.Fill(data, 0xFF); // size set to INT.MAX + + var tel = parser.Parse(new ReliancePacket(data)); + Assert.Null(tel); + } + + [Fact] + public void TestInValidLengthPacket() + { + // Test that a valid size field can be processed + var parser = PacketParserFactory.Instance.Create(); + var data = new byte[9]; // less than decalred size (10) + data[4] = 10; // size set to 10 + + var tel = parser.Parse(new ReliancePacket(data)); + Assert.Null(tel); + } + + [Fact] + public void TestValidLengthPacket() + { + // Test that a valid size field can be processed + var parser = PacketParserFactory.Instance.Create(); + var data = new byte[240]; // arbitrary size + data[4] = 10; // size set to 10 + + var tel = parser.Parse(new ReliancePacket(data)); + Assert.NotNull(tel); + + Assert.Equal(0, tel.PowerUpCount); + Assert.Equal(0, tel.ResetCmdCount); + + tel.PowerUpCount++; + tel.ResetCmdCount++; + + Assert.Equal(1, tel.PowerUpCount); + Assert.Equal(1, tel.ResetCmdCount); + } + } +} diff --git a/PTIRelianceLib.Tests/Telemetry/PowerupTelemetryParserTests.cs b/PTIRelianceLib.Tests/Telemetry/PowerupTelemetryParserTests.cs new file mode 100644 index 0000000..a4ecdea --- /dev/null +++ b/PTIRelianceLib.Tests/Telemetry/PowerupTelemetryParserTests.cs @@ -0,0 +1,37 @@ +using Xunit; + +namespace PTIRelianceLib.Tests.Telemetry +{ + using PTIRelianceLib.Telemetry; + using PTIRelianceLib.Transport; + + public class PowerupTelemetryParserTests + { + [Fact] + public void TestIsRegistered() + { + PacketParserFactory.Instance.Create(); + } + + [Fact] + public void TestNullPacket() + { + // Test the null object is returned + var parser = PacketParserFactory.Instance.Create(); + var tel = parser.Parse(null); + Assert.Null(tel); + } + + [Fact] + public void TestValidLengthPacket() + { + // Test that a valid size field can be processed + var parser = PacketParserFactory.Instance.Create(); + var data = new byte[240]; // arbitrary size + data[4] = 10; // size set to 10 + + var tel = parser.Parse(new ReliancePacket(data)); + Assert.NotNull(tel); + } + } +} diff --git a/PTIRelianceLib/Internal/FixedArray.cs b/PTIRelianceLib/Internal/FixedArray.cs index 9bfc32f..c622cc4 100644 --- a/PTIRelianceLib/Internal/FixedArray.cs +++ b/PTIRelianceLib/Internal/FixedArray.cs @@ -10,7 +10,6 @@ namespace PTIRelianceLib { using System.Collections.Generic; using System.Collections.Immutable; - using System.Linq; /// /// A read-only fixed length array diff --git a/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs b/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs index f155955..2c02003 100644 --- a/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs +++ b/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs @@ -31,7 +31,8 @@ public class LifetimeTelemetry : PowerupTelemetry /// public override byte[] Serialize() { - throw new System.NotImplementedException(); + // TODO implement telemetry serializer + return new byte[0]; } } @@ -59,8 +60,14 @@ public override LifetimeTelemetry Parse(IPacket packet) tel.StructRevision = reader.ReadInt32(); tel.StructSize = reader.ReadInt32(); + // is the size logical? + if (tel.StructSize == 0 || tel.StructSize == -1) + { + return null; + } + // Ensure there is enough data left in the stream to read - if (packet.Count - 8 < tel.StructSize) + if (packet.Count - 8 <= tel.StructSize) { return null; } diff --git a/PTIRelianceLib/Transport/PacketParserFactory.cs b/PTIRelianceLib/Transport/PacketParserFactory.cs index 641d875..7685536 100644 --- a/PTIRelianceLib/Transport/PacketParserFactory.cs +++ b/PTIRelianceLib/Transport/PacketParserFactory.cs @@ -51,6 +51,7 @@ private PacketParserFactory() { } /// /// Class to generate /// Parser class + /// Thrown if T or K are not instantiable public void Register() where T : IParseable where TK : IParseAs @@ -76,6 +77,8 @@ public void Register() /// /// /// + /// Raised if a parser for type T has + /// not been registered. public IParseAs Create(params object[] parameters) where T : IParseable { From dbbe8a72b6255027aab2e05ba1dadcc0fceb36be Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 19 Jun 2018 12:56:45 -0700 Subject: [PATCH 07/25] Add StructuredReaderTests --- .../IO/StructuredReaderTests.cs | 94 +++++++++++++++++++ PTIRelianceLib.Tests/MutableReliancePacket.cs | 4 + .../Telemetry/LifetimeTelemetryParserTests.cs | 1 - PTIRelianceLib/Telemetry/PowerupTelemetry.cs | 3 +- 4 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 PTIRelianceLib.Tests/IO/StructuredReaderTests.cs diff --git a/PTIRelianceLib.Tests/IO/StructuredReaderTests.cs b/PTIRelianceLib.Tests/IO/StructuredReaderTests.cs new file mode 100644 index 0000000..6a4a297 --- /dev/null +++ b/PTIRelianceLib.Tests/IO/StructuredReaderTests.cs @@ -0,0 +1,94 @@ +using System; +using System.Linq; +using Xunit; + +namespace PTIRelianceLib.Tests.IO +{ + using PTIRelianceLib.IO; + using PTIRelianceLib.IO.Internal; + using PTIRelianceLib.Transport; + + public class StructuredReaderTests + { + private static readonly object MTestLock = new object(); + + private readonly HidDeviceConfig _mConfig; + private readonly FakeNativeMethods _mNativeMock; + private readonly HidPort _mPort; + public const int VendorId = 0x0425; + public const int ProductId = 0x8147; + + public StructuredReaderTests() + { + _mNativeMock = new FakeNativeMethods(); + + // Reliance will "always" use report lengths of 34 bytes + _mConfig = new HidDeviceConfig + { + VendorId = VendorId, + ProductId = ProductId, + InReportLength = 34, + OutReportLength = 34, + InReportId = 2, + OutReportId = 1, + NativeHid = _mNativeMock + }; + + _mPort = new HidPort(_mConfig); + } + + [Fact] + public void TestCtor() + { + lock (MTestLock) + { + _mNativeMock.GetNextResponse = (d) => GenerateHidData(0xAA); + new StructuredReader(_mPort); + } + } + + + [Fact] + public void TestReadNak() + { + lock (MTestLock) + { + _mNativeMock.GetNextResponse = (d) => GenerateHidData(0xAC); + var reader = new StructuredReader(_mPort); + var resp = reader.Read(1, 2, 3); + Assert.True(resp.IsEmpty); + } + } + + [Fact] + public void TestReadAck() + { + lock (MTestLock) + { + var count = 0; + // Only return Ack on first pass + _mNativeMock.GetNextResponse = (d) => GenerateHidData((byte) (count++ == 0 ? 0xAA : 0xAC)); + var reader = new StructuredReader(_mPort); + var resp = reader.Read(1, 2, 3); + Assert.False(resp.IsEmpty); + } + } + + /// + /// Generates a valid inreport + /// + /// Payload to pack + /// + private byte[] GenerateHidData(params byte[] payload) + { + var packet = new ReliancePacket(payload); + payload = packet.Package().GetBytes(); + + var buff = new byte[_mConfig.InReportLength]; + buff[0] = _mConfig.InReportId; + buff[1] = (byte) payload.Length; + Array.Copy(payload, 0, buff, 2, Math.Min(payload.Length, buff.Length - 2)); + return buff; + } + } +} \ No newline at end of file diff --git a/PTIRelianceLib.Tests/MutableReliancePacket.cs b/PTIRelianceLib.Tests/MutableReliancePacket.cs index a3a3e51..d84bb3c 100644 --- a/PTIRelianceLib.Tests/MutableReliancePacket.cs +++ b/PTIRelianceLib.Tests/MutableReliancePacket.cs @@ -13,6 +13,10 @@ namespace PTIRelianceLib.Tests internal class MutableReliancePacket : ReliancePacket { + public MutableReliancePacket() + { + } + internal MutableReliancePacket(byte[] data) : base(data) { } diff --git a/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs b/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs index 14eb2aa..cd8f16d 100644 --- a/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs +++ b/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using Xunit; namespace PTIRelianceLib.Tests.Telemetry diff --git a/PTIRelianceLib/Telemetry/PowerupTelemetry.cs b/PTIRelianceLib/Telemetry/PowerupTelemetry.cs index 1ae18f1..e65e5f5 100644 --- a/PTIRelianceLib/Telemetry/PowerupTelemetry.cs +++ b/PTIRelianceLib/Telemetry/PowerupTelemetry.cs @@ -128,7 +128,8 @@ public class PowerupTelemetry : IParseable /// public virtual byte[] Serialize() { - throw new System.NotImplementedException(); + // TODO implement PowerupTelemetry serializer + return new byte[0]; } } From 9782a297fd276fcc0c1f5c7317eb2a84d7bef13d Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Thu, 21 Jun 2018 10:13:17 -0700 Subject: [PATCH 08/25] Fix errors in documentation Sample_05 --- docsource/tutorials/Sample_05.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docsource/tutorials/Sample_05.cs b/docsource/tutorials/Sample_05.cs index cd20d11..2ff74b1 100644 --- a/docsource/tutorials/Sample_05.cs +++ b/docsource/tutorials/Sample_05.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using PTIRelianceLib; using PTIRelianceLib.Imaging; @@ -9,21 +10,19 @@ class Program { static void Main(string[] args) { - - // Wrap our printer in using so it gets disposed on properly + // Wrap our printer in using so it gets disposed on properly using (var printer = new ReliancePrinter()) { var logos = new List { - BinaryFile.From("my_logo_a.jpg"), - BinaryFile.From("my_logo_b.png"), - BinaryFile.From("another.bmp"), + BinaryFile.From("index.jpg"), + BinaryFile.From("index2.jpg"), }; var config = LogoStorageConfig.Default; config.Algorithm = DitherAlgorithms.Atkinson; - var result = printer.StoreLogos(logos, new ConsoleProgressBar(), config); + var result = printer.StoreLogos(logos, new DevNullMonitor(), config); Console.WriteLine("Write Result: {0}", result); // use logo index to recall and print @@ -34,8 +33,7 @@ static void Main(string[] args) // Give ~3 seconds to print Thread.Sleep(3 * 1000); } - } } } -} +} \ No newline at end of file From a44548838cf9c7503eff497158d4aa98b2bceddf Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Thu, 21 Jun 2018 10:13:39 -0700 Subject: [PATCH 09/25] Add more imaging tests --- .../Imaging/BasePrintLogoTests.cs | 59 ++++++++++++++++++ .../Imaging/DitherFactoryTests.cs | 9 ++- PTIRelianceLib.Tests/Imaging/ImageExtTests.cs | 6 ++ .../Imaging/LogoStorageConfigTests.cs | 39 ++++++++++++ .../Properties/Resources.Designer.cs | 10 +++ .../Properties/Resources.resx | 3 + .../Resources/gray_floydsteinbergsfalse.bmp | Bin 0 -> 388 bytes PTIRelianceLib/Imaging/Dither.cs | 11 +--- PTIRelianceLib/Imaging/LogoSize.cs | 9 --- 9 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs create mode 100644 PTIRelianceLib.Tests/Imaging/LogoStorageConfigTests.cs create mode 100644 PTIRelianceLib.Tests/Resources/gray_floydsteinbergsfalse.bmp diff --git a/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs b/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs new file mode 100644 index 0000000..c24052a --- /dev/null +++ b/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using Xunit; + +namespace PTIRelianceLib.Tests.Imaging +{ + using System.Drawing; + using Properties; + using PTIRelianceLib.Imaging; + + public class BasePrintLogoTests + { + [Fact] + public void TestCtor() + { + var bitmap = BinaryFile.From(Resources.black_bitmap); + var logo = new BasePrintLogo(bitmap); + + // Dimensions should be unchanged, 48x48 + Assert.Equal(48, logo.IdealHeight); + Assert.Equal(48, logo.IdealWidth); + + // Max width/height were not specified, should be 0 + Assert.Equal(0, logo.MaxHeight); + Assert.Equal(0, logo.MaxWidth); + + // Valid data was passed in, ImageData should be valid too + Assert.NotNull(logo.ImageData); + } + + [Fact] + public void TestCtorNullBitmap() + { + Assert.Throws(()=>new BasePrintLogo(null)); + } + + [Fact] + public void TestApplyDithering() + { + var bitmap = BinaryFile.From(Resources.gray_bitmap); + var logo = new BasePrintLogo(bitmap); + + var startw = logo.IdealWidth; + var starth = logo.IdealHeight; + var predither = Crc32.ComputeChecksum(logo.ToBuffer()); + + logo.ApplyDithering(DitherAlgorithms.Atkinson); + + // Dimensions should be unchanged, 48x48 + Assert.Equal(starth, logo.IdealHeight); + Assert.Equal(startw, logo.IdealWidth); + + // Valid data was passed in, ImageData should be valid too + Assert.NotNull(logo.ImageData); + var postdither = Crc32.ComputeChecksum(logo.ToBuffer()); + Assert.NotEqual(predither, postdither); + } + } +} diff --git a/PTIRelianceLib.Tests/Imaging/DitherFactoryTests.cs b/PTIRelianceLib.Tests/Imaging/DitherFactoryTests.cs index b0653b5..ac3c76d 100644 --- a/PTIRelianceLib.Tests/Imaging/DitherFactoryTests.cs +++ b/PTIRelianceLib.Tests/Imaging/DitherFactoryTests.cs @@ -52,7 +52,14 @@ public void GetDithererFloydSteinbergFact() Assert.True(ImageTestHelpers.CompareCrc32(expected, dithered)); } - // TODO test false floyd steinberg + [Fact()] + public void GetDithererFloydSteinbergFalseFact() + { + var input = new BasePrintLogo(BinaryFile.From(Properties.Resources.gray_bitmap)).ImageData; + var expected = new BasePrintLogo(BinaryFile.From(Properties.Resources.gray_floydsteinbergsfalse)).ImageData; + var dithered = DitherFactory.GetDitherer(DitherAlgorithms.FloydSteinbergFalse).GenerateDithered(input); + Assert.True(ImageTestHelpers.CompareCrc32(expected, dithered)); + } [Fact()] public void GetDithererJarvisJudiceNinkeFact() diff --git a/PTIRelianceLib.Tests/Imaging/ImageExtTests.cs b/PTIRelianceLib.Tests/Imaging/ImageExtTests.cs index 3951d68..7596891 100644 --- a/PTIRelianceLib.Tests/Imaging/ImageExtTests.cs +++ b/PTIRelianceLib.Tests/Imaging/ImageExtTests.cs @@ -162,6 +162,12 @@ public void BitmapToLogoBufferSimpleFact() Assert.Equal(expectedBuff, actualBuff); } + [Fact] + public void TestNullBitmapToBuffer() + { + Assert.Empty(ImageExt.ToBuffer(null)); + } + /// /// Returns a list of type T with value repeat count times /// diff --git a/PTIRelianceLib.Tests/Imaging/LogoStorageConfigTests.cs b/PTIRelianceLib.Tests/Imaging/LogoStorageConfigTests.cs new file mode 100644 index 0000000..11b9954 --- /dev/null +++ b/PTIRelianceLib.Tests/Imaging/LogoStorageConfigTests.cs @@ -0,0 +1,39 @@ +using Xunit; + +namespace PTIRelianceLib.Tests.Imaging +{ + using PTIRelianceLib.Imaging; + + public class LogoStorageConfigTests + { + [Fact] + public void FailIfDefaultsChange() + { + var config = LogoStorageConfig.Default; + Assert.Equal(640, config.MaxPixelWidth); + Assert.Equal(127, config.Threshold); + Assert.Equal(DitherAlgorithms.None, config.Algorithm); + + // Make sure properties stay editable + config.MaxPixelWidth = 0; + config.Threshold = 0; + config.Algorithm = DitherAlgorithms.Burkes; + } + + [Fact] + public void FailIfDefaultsAreReadonly() + { + var config = LogoStorageConfig.Default; + + // Make sure properties stay editable + config.MaxPixelWidth = 0; + config.Threshold = 0; + config.Algorithm = DitherAlgorithms.Burkes; + + + Assert.Equal(0, config.MaxPixelWidth); + Assert.Equal(0, config.Threshold); + Assert.Equal(DitherAlgorithms.Burkes, config.Algorithm); + } + } +} diff --git a/PTIRelianceLib.Tests/Properties/Resources.Designer.cs b/PTIRelianceLib.Tests/Properties/Resources.Designer.cs index 9d8e92c..1e9d083 100644 --- a/PTIRelianceLib.Tests/Properties/Resources.Designer.cs +++ b/PTIRelianceLib.Tests/Properties/Resources.Designer.cs @@ -120,6 +120,16 @@ internal static byte[] gray_floydsteinbergs { } } + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] gray_floydsteinbergsfalse { + get { + object obj = ResourceManager.GetObject("gray_floydsteinbergsfalse", resourceCulture); + return ((byte[])(obj)); + } + } + /// /// Looks up a localized resource of type System.Byte[]. /// diff --git a/PTIRelianceLib.Tests/Properties/Resources.resx b/PTIRelianceLib.Tests/Properties/Resources.resx index d1f1e79..f958bee 100644 --- a/PTIRelianceLib.Tests/Properties/Resources.resx +++ b/PTIRelianceLib.Tests/Properties/Resources.resx @@ -136,6 +136,9 @@ ..\Resources\gray_floydsteinbergs.bmp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\gray_floydsteinbergsfalse.bmp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\gray_jjn.bmp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/PTIRelianceLib.Tests/Resources/gray_floydsteinbergsfalse.bmp b/PTIRelianceLib.Tests/Resources/gray_floydsteinbergsfalse.bmp new file mode 100644 index 0000000000000000000000000000000000000000..466f4884d55b2867cd1f44e03c381a3cee53b705 GIT binary patch literal 388 zcmV-~0ek+5P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0U1d|K~!i%-H`zf z!Y~X2`TwVjD~(a6ZN08dlVZub0gK~^my{OwE3K^kfbE)}=SBQ2U2+xe2V|8;EaX4= z2hO;NuMb#3)dMnvyM91qsUJHaGpP9i5v5+Nc*$Vq#Y;w!`mn-h3}RmRjNVcYR`ia} z=0)%5CGBSg37yOf63&+PvSKZqY+kH|v!s2j&=XEFFZ6`g(jHc%hsNebdT1r(vjQV% zWL{tdM@zYE@kkCfUp$hdq&&9Jf`iN#TCi8jVT&x;X}-vkS>-d8X6}PM*k~SX#p8D^ z(#&Nh-!ASf_ ipV>e}Zsr5=n9>{BFC!B=byte private static byte SafeByteCast(int val) { - if (val < 0) - { - val = 0; - } - else if (val > 255) - { - val = 255; - } - + val = Math.Max(0, val); + val = Math.Min(255, val); return (byte) val; } } diff --git a/PTIRelianceLib/Imaging/LogoSize.cs b/PTIRelianceLib/Imaging/LogoSize.cs index 80e17fd..71ff5af 100644 --- a/PTIRelianceLib/Imaging/LogoSize.cs +++ b/PTIRelianceLib/Imaging/LogoSize.cs @@ -34,14 +34,5 @@ internal class LogoSize /// Gets or sets the size in bytes for this logo /// public int SizeInBytes { get; set; } - - /// - /// Returns dimension string as WidthxHeight - /// - /// - public object GetBitmapSizeString() - { - return string.Format("{0}x{1}", WidthDots, Height); - } } } \ No newline at end of file From ff321bfc759ccac1bf2e0953917e14e9a1daea0a Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Thu, 21 Jun 2018 10:13:49 -0700 Subject: [PATCH 10/25] Add HidDeviceInfoTests --- .../IO/Internal/HidDeviceInfoTests.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 PTIRelianceLib.Tests/IO/Internal/HidDeviceInfoTests.cs diff --git a/PTIRelianceLib.Tests/IO/Internal/HidDeviceInfoTests.cs b/PTIRelianceLib.Tests/IO/Internal/HidDeviceInfoTests.cs new file mode 100644 index 0000000..5ee5e1d --- /dev/null +++ b/PTIRelianceLib.Tests/IO/Internal/HidDeviceInfoTests.cs @@ -0,0 +1,33 @@ +using System; +using System.Linq; +using Xunit; + +namespace PTIRelianceLib.Tests.IO.Internal +{ + using PTIRelianceLib.IO.Internal; + + public class HidDeviceInfoTests + { + [Fact] + public void TestToString() + { + var devinfo = new HidDeviceInfo + { + VendorId = 0x1234, + ProductId = 0x5678, + ManufacturerString = "Company", + ProductString = "Product", + Path = "/some/device&1234_5678/4325EACF" + }; + + var str = devinfo.ToString(); + Assert.NotNull(str); + + Assert.Contains(devinfo.ManufacturerString, str); + Assert.Contains(devinfo.ProductString, str); + Assert.Contains(devinfo.VendorId.ToString("X4"), str); + Assert.Contains(devinfo.ProductId.ToString("X4"), str); + } + } +} + From 2be1c4243cc5a7a6e9ffaa230abbcbf223126bd8 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Thu, 21 Jun 2018 10:22:00 -0700 Subject: [PATCH 11/25] Found and fixed logo to buffer bug If you wrap an assignment to BitmapData, when the assignment goes out of scope, the source bitmap will also be disposed --- PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs | 2 ++ PTIRelianceLib/Imaging/BasePrintLogo.cs | 10 ++-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs b/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs index c24052a..02ca5ed 100644 --- a/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs +++ b/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs @@ -44,6 +44,8 @@ public void TestApplyDithering() var starth = logo.IdealHeight; var predither = Crc32.ComputeChecksum(logo.ToBuffer()); + // If this fails, someone dispose of the bitmap along the way. + // Look for "using" statements to fix logo.ApplyDithering(DitherAlgorithms.Atkinson); // Dimensions should be unchanged, 48x48 diff --git a/PTIRelianceLib/Imaging/BasePrintLogo.cs b/PTIRelianceLib/Imaging/BasePrintLogo.cs index 3d8196f..d89a078 100644 --- a/PTIRelianceLib/Imaging/BasePrintLogo.cs +++ b/PTIRelianceLib/Imaging/BasePrintLogo.cs @@ -105,14 +105,8 @@ public void ApplyDithering(DitherAlgorithms ditherAlgorithm, byte threshhold = 1 /// public byte[] ToBuffer() { - if (ImageData == null) - { - return new byte[0]; - } - using (var bitmap = ImageData) - { - return bitmap.ToLogoBuffer(); - } + // Do not wrap in "using" statement as this will will dispose ImageData on closure + return ImageData == null ? new byte[0] : ImageData.ToLogoBuffer(); } /// From ca3b8fc4982280a45f7431643f2176cf175f2ed6 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Thu, 21 Jun 2018 10:34:37 -0700 Subject: [PATCH 12/25] Split up RElConfigUpdater reader so it is easier to understand --- .../Imaging/BasePrintLogoTests.cs | 6 +- .../Configuration/RElConfigUpdater.cs | 109 ++++++++++++------ 2 files changed, 77 insertions(+), 38 deletions(-) diff --git a/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs b/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs index 02ca5ed..e4762c0 100644 --- a/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs +++ b/PTIRelianceLib.Tests/Imaging/BasePrintLogoTests.cs @@ -1,10 +1,8 @@ using System; -using System.Linq; using Xunit; namespace PTIRelianceLib.Tests.Imaging { - using System.Drawing; using Properties; using PTIRelianceLib.Imaging; @@ -31,7 +29,7 @@ public void TestCtor() [Fact] public void TestCtorNullBitmap() { - Assert.Throws(()=>new BasePrintLogo(null)); + Assert.Throws(() => new BasePrintLogo(null)); } [Fact] @@ -58,4 +56,4 @@ public void TestApplyDithering() Assert.NotEqual(predither, postdither); } } -} +} \ No newline at end of file diff --git a/PTIRelianceLib/Configuration/RElConfigUpdater.cs b/PTIRelianceLib/Configuration/RElConfigUpdater.cs index 615cd20..4395be4 100644 --- a/PTIRelianceLib/Configuration/RElConfigUpdater.cs +++ b/PTIRelianceLib/Configuration/RElConfigUpdater.cs @@ -140,27 +140,35 @@ public ReturnCodes WriteConfiguration(BinaryFile file) public BinaryFile ReadConfiguration() { var config = new RELConfig(); - - var serial = GetConfig(_mPrinter, RelianceCommands.GetSerialConfig); - if (serial == null) + if (!FillSerialConfig(ref config)) { - // Read failure return BinaryFile.From(new byte[0]); } - config.BaudRate = serial.BaudRate; - config.Handshake = serial.Handshake; - config.Databits = serial.Databits; - config.Stopbits = serial.Stopbits; - config.Parity = serial.Parity; + FillGeneralConfig(ref config); + FillFontConfig(ref config); + FillBezelConfig(ref config); + using (var stream = new MemoryStream()) + { + config.Save(stream); + return BinaryFile.From(stream.GetBuffer()); + } + } + + /// + /// Reads in and fill config with all general parameters + /// + /// Configuration to fill + private void FillGeneralConfig(ref RELConfig config) + { config.Quality = (ReliancePrintQuality)(GetConfig(_mPrinter, RelianceCommands.GetPrintQuality) ?? 0); config.RetractEnabled = GetConfig(_mPrinter, RelianceCommands.GetRetractEnabled) == 1; config.Ejector = (RelianceEjectorMode)(GetConfig(_mPrinter, RelianceCommands.GetEjectorMode) ?? 0); config.TicketTimeout = GetConfig(_mPrinter, RelianceCommands.GetTicketTimeoutPeriod) ?? 5; - config.TicketTimeoutAction = (TicketTimeoutAction) (GetConfig(_mPrinter, RelianceCommands.GetTimeoutAction) ?? 0); - config.NewTicketAction = (NewTicketAction) (GetConfig(_mPrinter, RelianceCommands.GetNewTicketAction) ?? 1); + config.TicketTimeoutAction = (TicketTimeoutAction)(GetConfig(_mPrinter, RelianceCommands.GetTimeoutAction) ?? 0); + config.NewTicketAction = (NewTicketAction)(GetConfig(_mPrinter, RelianceCommands.GetNewTicketAction) ?? 1); config.PresentLength = GetConfig(_mPrinter, RelianceCommands.GetPresentLen) ?? 48; config.CRLFEnabled = GetConfig(_mPrinter, RelianceCommands.GetCRLFConf) == 1; config.PrintDensity = GetConfig(_mPrinter, RelianceCommands.GetPrintDensity) ?? 100; @@ -168,11 +176,45 @@ public BinaryFile ReadConfiguration() config.AutocutTimeout = GetConfig(_mPrinter, RelianceCommands.AutocutSub, 3) ?? 1; config.PaperWidth = PaperSizeUtils.FromByte((byte)(GetConfig(_mPrinter, RelianceCommands.PaperSizeSub, 0) ?? 80)); + var configrev = GetConfig(_mPrinter, RelianceCommands.GeneralConfigSub, 0x0F); + config.Version = configrev.Version; + config.Revision = configrev.Revision; + } + + /// + /// Reads in and fill config with all bezel parameters + /// + /// Configuration to fill + private void FillBezelConfig(ref RELConfig config) + { + var bezels = new List(); + for (byte i = 0; i < 4; ++i) + { + // [bezel sub] [3] [printer state (i)] + bezels.Add(GetConfig(_mPrinter, RelianceCommands.BezelSub, 3, i)); + } + + config.BezelIdleDutyCycle = bezels[0].DutyCycle; + config.BezelIdleInterval = bezels[0].FlashInterval; + config.BezelPrintingDutyCycle = bezels[1].DutyCycle; + config.BezelPrintingInterval = bezels[1].FlashInterval; + config.BezelPresentedDutyCycle = bezels[2].DutyCycle; + config.BezelPresentedInterval = bezels[2].FlashInterval; + config.BezelEjectingDutyCycle = bezels[3].DutyCycle; + config.BezelEjectingInterval = bezels[3].FlashInterval; + } + + /// + /// Reads in and fills config with all font parameters + /// + /// Configuration to fill + private void FillFontConfig(ref RELConfig config) + { var font = GetConfig(_mPrinter, RelianceCommands.FontSub, 0); config.FontWhich = font.FontWhich; config.FontSize = font.FontSize; config.DefaultCodepage = font.CodePage; - config.FontScalingMode = (RelianceScalarMode) (GetConfig(_mPrinter, RelianceCommands.FontSub, 7) ?? 1); + config.FontScalingMode = (RelianceScalarMode)(GetConfig(_mPrinter, RelianceCommands.FontSub, 7) ?? 1); var codepages = _mPrinter.GetInstalledCodepages().ToList(); config.Codepage1 = codepages.Count > 0 ? codepages[0] : font.CodePage; @@ -185,35 +227,34 @@ public BinaryFile ReadConfiguration() config.IsPaperSlackEnabeld = GetConfig(_mPrinter, RelianceCommands.GeneralConfigSub, 3) == 1; config.IsUniqueUSBSNEnabled = GetConfig(_mPrinter, RelianceCommands.GeneralConfigSub, 7) == 1; - var bezels = new List(); - for (byte i = 0; i < 4; ++i) - { - // [bezel sub] [3] [printer state (i)] - bezels.Add(GetConfig(_mPrinter, RelianceCommands.BezelSub, 3, i)); + } + + /// + /// Reads Serial configuration into config reference + /// + /// Configuration to fill + /// True on succes, else false + private bool FillSerialConfig(ref RELConfig config) + { + var serial = GetConfig(_mPrinter, RelianceCommands.GetSerialConfig); + if (serial == null) + { + // Read failure + return false; } - config.BezelIdleDutyCycle = bezels[0].DutyCycle; - config.BezelIdleInterval = bezels[0].FlashInterval; - config.BezelPrintingDutyCycle = bezels[1].DutyCycle; - config.BezelPrintingInterval = bezels[1].FlashInterval; - config.BezelPresentedDutyCycle = bezels[2].DutyCycle; - config.BezelPresentedInterval = bezels[2].FlashInterval; - config.BezelEjectingDutyCycle = bezels[3].DutyCycle; - config.BezelEjectingInterval = bezels[3].FlashInterval; + config.BaudRate = serial.BaudRate; + config.Handshake = serial.Handshake; + config.Databits = serial.Databits; + config.Stopbits = serial.Stopbits; + config.Parity = serial.Parity; + var xonxoff = GetConfig(_mPrinter, RelianceCommands.GeneralConfigSub, 0x0B); config.XonCode = xonxoff.Xon; config.XoffCode = xonxoff.Xoff; - var configrev = GetConfig(_mPrinter, RelianceCommands.GeneralConfigSub, 0x0F); - config.Version = configrev.Version; - config.Revision = configrev.Revision; - - using (var stream = new MemoryStream()) - { - config.Save(stream); - return BinaryFile.From(stream.GetBuffer()); - } + return true; } /// From 5bc2c0e3f5808472a897d8126cebda715dbca308 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Thu, 21 Jun 2018 10:55:18 -0700 Subject: [PATCH 13/25] Add more debug level logging --- PTIRelianceLib/ReliancePrinter.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/PTIRelianceLib/ReliancePrinter.cs b/PTIRelianceLib/ReliancePrinter.cs index f287ebb..dcecb07 100644 --- a/PTIRelianceLib/ReliancePrinter.cs +++ b/PTIRelianceLib/ReliancePrinter.cs @@ -234,6 +234,8 @@ public Revlev GetFirmwareRevision() return new Revlev(); } + Log.Debug("Requesting revision level"); + var cmd = new ReliancePacket(RelianceCommands.GetRevlev); // "Self" param specifies we want revlev for running application cmd.Add(0x10); @@ -288,6 +290,8 @@ public ReturnCodes Ping() return ReturnCodes.DeviceNotConnected; } + Log.Debug("Sending Ping"); + var cmd = new ReliancePacket(RelianceCommands.Ping); var resp = Write(cmd); return resp.GetPacketType() == PacketTypes.PositiveAck ? ReturnCodes.Okay : ReturnCodes.ExecutionFailure; @@ -313,6 +317,7 @@ public ReturnCodes Reboot() try { + Log.Debug("Rebooting printer"); var cmd = new ReliancePacket(RelianceCommands.Reboot); Write(cmd); @@ -519,7 +524,7 @@ public LifetimeTelemetry GetLifetimeTelemetry() /// Returns the telemetry data of this printer since last power up /// /// Requires firmware 1.28+. Older firmware will result in null result. - /// Powerup telemetry or null if read failure or unsupported firmware + /// Powerup telemetry or null if read failure or unsupported firmware public PowerupTelemetry GetPowerupTelemetry() { return GetFirmwareRevision() < new Revlev("1.28") ? @@ -547,6 +552,8 @@ protected PowerupTelemetry ReadTelemetry(byte type, ushort offset, ushort count) request.Add(count.ToBytesBE()); } + Log.Debug("Requesting telemetry info"); + // Request permission var resp = Write(request); if (resp.GetPacketType() != PacketTypes.PositiveAck) @@ -574,6 +581,7 @@ internal string GetAppId() return string.Empty; } + Log.Debug("Requesting app id"); var cmd = new ReliancePacket((byte) RelianceCommands.GetBootId, 0x10); var resp = Write(cmd); return PacketParserFactory.Instance.Create().Parse(resp).Value; @@ -590,6 +598,7 @@ internal ReturnCodes EnterBootloader() return ReturnCodes.DeviceNotConnected; } + Log.Debug("Entering bootloader"); var resp = Write(RelianceCommands.SetBootMode, 0x21); return resp.GetPacketType() != PacketTypes.PositiveAck ? ReturnCodes.FailedBootloaderEntry : Reboot(); From 6e026d078fc4261b346e54ea0ed81e49b5d69fdd Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Thu, 21 Jun 2018 10:55:31 -0700 Subject: [PATCH 14/25] Add new telemtry commands CLI flags --- RelianceCLI/Program.cs | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/RelianceCLI/Program.cs b/RelianceCLI/Program.cs index 4170d66..858dfe6 100644 --- a/RelianceCLI/Program.cs +++ b/RelianceCLI/Program.cs @@ -144,6 +144,18 @@ private static void Run(Options opts) } } + if (opts.LifetimeTelemetry) + { + var resp = printer.GetLifetimeTelemetry(); + Console.WriteLine("Total power-on count: {0}", resp?.PowerUpCount); + } + + if (opts.StartupTelemetry) + { + var resp = printer.GetPowerupTelemetry(); + Console.WriteLine("Tickets printed since last powerup: {0}", resp?.TicketCount); + } + if (opts.GetStatus) { var status = printer.GetStatus(); @@ -174,11 +186,16 @@ private struct Options public string SaveConfigPath; + public bool LifetimeTelemetry; + + public bool StartupTelemetry; + public static Options Parse(IEnumerable args) { var opts = new Options(); Action nextCapture = null; + var lastSwitch = string.Empty; foreach (var str in args) { @@ -197,11 +214,13 @@ public static Options Parse(IEnumerable args) break; case "-f": case "--firmware": + lastSwitch = str; nextCapture = s => opts.FirmwareFilePath = s; break; case "-c": case "--config": + lastSwitch = str; nextCapture = s => opts.ConfigFilePath = s; break; @@ -212,6 +231,7 @@ public static Options Parse(IEnumerable args) case "-l": case "--logo": + lastSwitch = str; nextCapture = s => opts.LogoFilePath = s; break; @@ -222,9 +242,19 @@ public static Options Parse(IEnumerable args) case "-g": case "--get-config": + lastSwitch = str; nextCapture = s => opts.SaveConfigPath = s; break; + case "-t": + case "--startup-telem": + opts.StartupTelemetry = true; + break; + case "-T": + case "--lifetime-telem": + opts.LifetimeTelemetry = true; + break; + default: opts.Message = string.Format("Unknown switch: {0}", str); return opts; @@ -234,7 +264,7 @@ public static Options Parse(IEnumerable args) if (nextCapture != null) { - opts.Message = "Incomplete command line switch"; + opts.Message = string.Format("Incomplete command line switch: {0}", lastSwitch); } else { @@ -257,6 +287,8 @@ public static string Usage() "FLAGS\n" + "\t-r,--revision\t\tRead and display printer firmware revision\n" + "\t-s,--status\t\tRead and display printer status\n" + + "\t-t,--startup-telem\t\tRead startup telemetry\n" + + "\t-T,--lifetime-telem\t\tRead lifetime telemetry\n" + "\t-p,--power\t\tReboot printer immediately\n"; } } From 73f44a1d427850811c0b2bae8f3d4982ee2a200b Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Mon, 25 Jun 2018 10:17:38 -0700 Subject: [PATCH 15/25] Update Telem API Update API to reflect actual firwmare impemlementation --- .gitignore | 1 + PTIRelianceLib/IO/StructuredReader.cs | 10 +- PTIRelianceLib/ReliancePrinter.cs | 102 ++++++++++++++------- PTIRelianceLib/Telemetry/TelemetryTypes.cs | 25 +++++ RelianceCLI/publish_debug.bat | 1 + 5 files changed, 103 insertions(+), 36 deletions(-) create mode 100644 PTIRelianceLib/Telemetry/TelemetryTypes.cs create mode 100644 RelianceCLI/publish_debug.bat diff --git a/.gitignore b/.gitignore index d001e67..53bb2a6 100644 --- a/.gitignore +++ b/.gitignore @@ -262,3 +262,4 @@ __pycache__/ /PTIRelianceLib/build/* _site/ nuget/ +/RelianceCLI/build/ diff --git a/PTIRelianceLib/IO/StructuredReader.cs b/PTIRelianceLib/IO/StructuredReader.cs index 5584b8d..c6fd251 100644 --- a/PTIRelianceLib/IO/StructuredReader.cs +++ b/PTIRelianceLib/IO/StructuredReader.cs @@ -24,12 +24,18 @@ public StructuredReader(IPort device) _device = device; } - public IPacket Read(params byte[] preamble) + /// + /// Reads readlen bytes using preamble read command + /// + /// Total bytes to read (not including packet overhead) + /// Command the reads data from data + /// Data that was read + public IPacket Read(int readLen, params byte[] preamble) { var buffer = new List(); var sequenceNum = 0; - while (true) + while (buffer.Count < readLen) { var cmd = _device.Package(preamble); var seq = sequenceNum.ToBytesBE(); diff --git a/PTIRelianceLib/ReliancePrinter.cs b/PTIRelianceLib/ReliancePrinter.cs index dcecb07..efd67e5 100644 --- a/PTIRelianceLib/ReliancePrinter.cs +++ b/PTIRelianceLib/ReliancePrinter.cs @@ -236,11 +236,14 @@ public Revlev GetFirmwareRevision() Log.Debug("Requesting revision level"); - var cmd = new ReliancePacket(RelianceCommands.GetRevlev); - // "Self" param specifies we want revlev for running application - cmd.Add(0x10); + // 0x10: specifies we want revlev for running application + var cmd = _mPort.Package((byte)RelianceCommands.GetRevlev, 0x10); + var resp = Write(cmd); - return PacketParserFactory.Instance.Create().Parse(resp); + var rev = PacketParserFactory.Instance.Create().Parse(resp); + + Log.Debug("Found firmware revision {0}", rev); + return rev; } /// @@ -422,7 +425,8 @@ public IEnumerable GetInstalledCodepages() /// You probably want to configure this yourself. /// Return Code /// Thrown if logodata is null - public ReturnCodes StoreLogos(IList logoData, IProgressMonitor monitor, LogoStorageConfig storageConfig) + public ReturnCodes StoreLogos(IList logoData, IProgressMonitor monitor, + LogoStorageConfig storageConfig) { if (logoData == null) { @@ -442,12 +446,12 @@ public ReturnCodes StoreLogos(IList logoData, IProgressMonitor monit } var logoBank = new RELLogoBank(); - var ditheredLogos = logoData.Select(logo => + var ditheredLogos = logoData.Select(logo => new BasePrintLogo(logo, maxWidth: storageConfig.MaxPixelWidth)).Cast().ToList(); ditheredLogos.ForEach(x => x.ApplyDithering(storageConfig.Algorithm, storageConfig.Threshold)); - + // MakeHeaders locks in our scaling and dithering options - foreach(var header in logoBank.MakeHeaders(ditheredLogos)) + foreach (var header in logoBank.MakeHeaders(ditheredLogos)) { // sub command 2: Set Logo header var cmd = _mPort.Package((byte) RelianceCommands.LogoSub, 0x02); @@ -501,6 +505,7 @@ public ReturnCodes PrintLogo(int index) { return ReturnCodes.InvalidRequestPayload; } + // 7 == print logo sub command Write(RelianceCommands.LogoSub, 7, (byte) index); return ReturnCodes.Okay; @@ -509,15 +514,18 @@ public ReturnCodes PrintLogo(int index) /// /// Returns the telemetry data over the lifetime of this printer /// + /// Requires firmware 1.28+. Older firmware will result in null result. /// LifetimeTelemtry data since printer left factory public LifetimeTelemetry GetLifetimeTelemetry() { - var tel = GetFirmwareRevision() < new Revlev("1.28") ? - // Revert to legacy mode )always NV) for older firmware - ReadTelemetry(0, 0, 0) : - // 1: read non-volatile version, from start to end - ReadTelemetry(1, 0, 0); - return (LifetimeTelemetry) tel; + if (GetFirmwareRevision() < new Revlev("1.28")) + { + // unsupported + return null; + } + + // 1: read non-volatile version, from start to end + return (LifetimeTelemetry) ReadTelemetry(TelemetryTypes.Lifetime); } /// @@ -527,49 +535,75 @@ public LifetimeTelemetry GetLifetimeTelemetry() /// Powerup telemetry or null if read failure or unsupported firmware public PowerupTelemetry GetPowerupTelemetry() { - return GetFirmwareRevision() < new Revlev("1.28") ? - // Not supported on older firmware - null : - // 2: read volatile version, from start to end - ReadTelemetry(2, 0, 0); + // 2: read volatile version, from start to end + return GetFirmwareRevision() < new Revlev("1.28") ? null : ReadTelemetry(TelemetryTypes.Powerup); } /// /// Reads the specified telemtry data block /// - /// Type of telemetry to request. Set 0 for legacy mode - /// Position to start reading from, 0 base - /// How many bytes to read. 0 to read all of it. + /// Type of telemetry to request /// - protected PowerupTelemetry ReadTelemetry(byte type, ushort offset, ushort count) + internal PowerupTelemetry ReadTelemetry(TelemetryTypes type) { - // Build permission request - var request = _mPort.Package((byte)RelianceCommands.TelemtrySub, 0); - if (type != 0) + Log.Debug("Requesting telemetry info"); + + var readlen = ReadTelemetrySize(type); + if (readlen <= 0) { - request.Add(type); - request.Add(offset.ToBytesBE()); - request.Add(count.ToBytesBE()); + // Bad read size + return null; } - Log.Debug("Requesting telemetry info"); + // Build permission request. 0: request data + var request = _mPort.Package((byte) RelianceCommands.TelemtrySub, 0, (byte) type); - // Request permission + // Read entire data chunk from start of data (0->readlen) + request.Add(((ushort) 0).ToBytesBE()); + request.Add(((ushort) readlen).ToBytesBE()); + + // Request permission var resp = Write(request); if (resp.GetPacketType() != PacketTypes.PositiveAck) { + // Permission denied return null; } - // 1: read data request + // 1: repeatedly read data request var preamble = new byte[] {(byte) RelianceCommands.TelemtrySub, 1}; - // Execute read + // Execute a structured read var reader = new StructuredReader(_mPort); - resp = reader.Read(preamble); + resp = reader.Read(readlen, preamble); return PacketParserFactory.Instance.Create().Parse(resp); } + /// + /// Reads the size of the specified telemetry struct from target + /// + /// Telemetry format to request sizeof + /// Integer size in bytes, -1 on error + internal int ReadTelemetrySize(TelemetryTypes type) + { + // 4: read data size request + var getSize = _mPort.Package((byte) RelianceCommands.TelemtrySub, 4, (byte) type); + var resp = Write(getSize); + if (resp.GetPacketType() != PacketTypes.PositiveAck) + { + return -1; + } + + var readlen = (int) PacketParserFactory.Instance.Create().Parse(resp).Value; + if (readlen <= 0) + { + // Invalid data read length + return -1; + } + + return readlen; + } + /// /// Returns firmware application id string or empty on error /// diff --git a/PTIRelianceLib/Telemetry/TelemetryTypes.cs b/PTIRelianceLib/Telemetry/TelemetryTypes.cs new file mode 100644 index 0000000..703c341 --- /dev/null +++ b/PTIRelianceLib/Telemetry/TelemetryTypes.cs @@ -0,0 +1,25 @@ +#region Header +// TelemetryTypes.cs +// PTIRelianceLib +// Cory Todd +// 25-06-2018 +// 7:40 AM +#endregion + +namespace PTIRelianceLib.Telemetry +{ + /// + /// Context of telemtry data + /// + internal enum TelemetryTypes + { + /// + /// Data is counted since factory manugacturing + /// + Lifetime = 0, + /// + /// Data is counted since last powerup + /// + Powerup = 1, + } +} \ No newline at end of file diff --git a/RelianceCLI/publish_debug.bat b/RelianceCLI/publish_debug.bat new file mode 100644 index 0000000..4a0144c --- /dev/null +++ b/RelianceCLI/publish_debug.bat @@ -0,0 +1 @@ + dotnet publish -c Release -o build --self-contained -r win10-x64 /p:Platform=x64 \ No newline at end of file From b6e83d988abc08064732b1facb46675db92ea1e9 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Mon, 25 Jun 2018 13:58:30 -0700 Subject: [PATCH 16/25] Finish telemetry API Include latest v3 struct support and unit tests Supports reading both lifetime and powerup telemetry objects Supports erasing all telemtry objects --- .../IO/StructuredReaderTests.cs | 6 +- .../Internal/FixedArrayTests.cs | 15 +++++ .../Properties/Resources.Designer.cs | 10 +++ .../Properties/Resources.resx | 3 + .../Telemetry/LifetimeTelemetryParserTests.cs | 8 +++ .../Telemetry/PowerupTelemetryParserTests.cs | 61 +++++++++++++++++- PTIRelianceLib/IO/StructuredReader.cs | 2 + PTIRelianceLib/Internal/FixedArray.cs | 11 +++- PTIRelianceLib/ReliancePrinter.cs | 14 +++- PTIRelianceLib/Telemetry/LifetimeTelemetry.cs | 8 +-- PTIRelianceLib/Telemetry/PowerupTelemetry.cs | 20 +++++- .../Telemetry/TicketLengthGroups.cs | 55 ++++++++++++++++ .../Telemetry/TicketPullTimeGroups.cs | 64 +++++++++++++++++++ 13 files changed, 264 insertions(+), 13 deletions(-) create mode 100644 PTIRelianceLib/Telemetry/TicketLengthGroups.cs create mode 100644 PTIRelianceLib/Telemetry/TicketPullTimeGroups.cs diff --git a/PTIRelianceLib.Tests/IO/StructuredReaderTests.cs b/PTIRelianceLib.Tests/IO/StructuredReaderTests.cs index 6a4a297..a24f43a 100644 --- a/PTIRelianceLib.Tests/IO/StructuredReaderTests.cs +++ b/PTIRelianceLib.Tests/IO/StructuredReaderTests.cs @@ -65,11 +65,9 @@ public void TestReadAck() { lock (MTestLock) { - var count = 0; - // Only return Ack on first pass - _mNativeMock.GetNextResponse = (d) => GenerateHidData((byte) (count++ == 0 ? 0xAA : 0xAC)); + _mNativeMock.GetNextResponse = (d) => GenerateHidData(0xAA, 1, 2, 3); var reader = new StructuredReader(_mPort); - var resp = reader.Read(1, 2, 3); + var resp = reader.Read(3, 1, 2, 3); Assert.False(resp.IsEmpty); } } diff --git a/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs b/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs index 193b6c6..7535f9d 100644 --- a/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs +++ b/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs @@ -38,5 +38,20 @@ public void TestSetData() Assert.Equal(len++, bf.Length); } } + + [Fact] + public void TestIndexer() + { + var farr = new FixedArray(3); + var obj1 = new object(); + var obj2 = new object(); + var obj3 = new object(); + + farr.SetData(obj1, obj2, obj3); + + Assert.Equal(obj1, farr[0]); + Assert.Equal(obj2, farr[1]); + Assert.Equal(obj3, farr[2]); + } } } diff --git a/PTIRelianceLib.Tests/Properties/Resources.Designer.cs b/PTIRelianceLib.Tests/Properties/Resources.Designer.cs index 1e9d083..a5e2f71 100644 --- a/PTIRelianceLib.Tests/Properties/Resources.Designer.cs +++ b/PTIRelianceLib.Tests/Properties/Resources.Designer.cs @@ -200,6 +200,16 @@ internal static byte[] red_bitmap { } } + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] telemetry_v3 { + get { + object obj = ResourceManager.GetObject("telemetry_v3", resourceCulture); + return ((byte[])(obj)); + } + } + /// /// Looks up a localized resource of type System.Byte[]. /// diff --git a/PTIRelianceLib.Tests/Properties/Resources.resx b/PTIRelianceLib.Tests/Properties/Resources.resx index f958bee..a704337 100644 --- a/PTIRelianceLib.Tests/Properties/Resources.resx +++ b/PTIRelianceLib.Tests/Properties/Resources.resx @@ -160,6 +160,9 @@ ..\Resources\red_bitmap.bmp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\telemetry.v3.bin;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\white_bitmap.bmp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs b/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs index cd8f16d..13596c5 100644 --- a/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs +++ b/PTIRelianceLib.Tests/Telemetry/LifetimeTelemetryParserTests.cs @@ -74,6 +74,14 @@ public void TestInValidLengthPacket() Assert.Null(tel); } + [Fact] + public void TestSerializeEmpty() + { + // Serialize is not implement, should be empty + var tel = new LifetimeTelemetry(); + Assert.Empty(tel.Serialize()); + } + [Fact] public void TestValidLengthPacket() { diff --git a/PTIRelianceLib.Tests/Telemetry/PowerupTelemetryParserTests.cs b/PTIRelianceLib.Tests/Telemetry/PowerupTelemetryParserTests.cs index a4ecdea..370de14 100644 --- a/PTIRelianceLib.Tests/Telemetry/PowerupTelemetryParserTests.cs +++ b/PTIRelianceLib.Tests/Telemetry/PowerupTelemetryParserTests.cs @@ -2,6 +2,9 @@ namespace PTIRelianceLib.Tests.Telemetry { + using System; + using System.Linq; + using System.Security.Cryptography; using PTIRelianceLib.Telemetry; using PTIRelianceLib.Transport; @@ -33,5 +36,61 @@ public void TestValidLengthPacket() var tel = parser.Parse(new ReliancePacket(data)); Assert.NotNull(tel); } + + [Fact] + public void TestSerializeEmpty() + { + // Serialize is not implement, should be empty + var tel = new PowerupTelemetry(); + Assert.Empty(tel.Serialize()); + } + + [Fact] + public void TestRev3Struct() + { + // Test that a valid response payload can be parsed + var parser = PacketParserFactory.Instance.Create(); + var data = Properties.Resources.telemetry_v3; + var tel = parser.Parse(new ReliancePacket(data)); + Assert.NotNull(tel); + + Assert.Equal(192, tel.AvgCutTime); + Assert.Equal(252, tel.AvgTimePresented); + Assert.Equal(0, tel.Button); + Assert.Equal(0, tel.CriticalErrorCount); + Assert.Equal(0, tel.CutterCount); + Assert.Equal(1, tel.HighErrorCount); + Assert.Equal(0, tel.JammedCount); + Assert.Equal(0, tel.OverheatedCount); + Assert.Equal(42688, tel.PaperMovedCount); + Assert.Equal(1, tel.PaperOut); + Assert.Equal(0, tel.PaperOutDuringPrint); + Assert.Equal(0, tel.PlatenOpenCount); + Assert.Equal(3, tel.StructRevision); + Assert.Equal(172, tel.StructSize); + + Assert.Equal(9, tel.TicketLengthLog.Size); + Assert.Equal(9, tel.TicketLengthLog.Count); + + var lens = new[] {28, 1, 0, 0, 0, 0, 1, 0, 1}; + var lenGroups = Enum.GetValues(typeof(TicketLengthGroups)).Cast().ToArray(); + for (var i = 0; i < lens.Length; ++i) + { + Assert.Equal(lens[i], tel.TicketLengthLog[i]); + Assert.Equal(lens[i], tel.TicketCountsByLength(lenGroups[i])); + } + + var times = new[] {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + var timeGroups = Enum.GetValues(typeof(TicketPullTimeGroups)).Cast().ToArray(); + for (var i = 0; i < times.Length; ++i) + { + Assert.Equal(times[i], tel.TicketPullTimeLog[i]); + Assert.Equal(times[i], tel.TicketCountByTimeToPull(timeGroups[i])); + } + + Assert.Equal(31, tel.TicketsEjected); + Assert.Equal(5, tel.TicketsPulled); + Assert.Equal(0, tel.TicketsRetracted); + } } -} +} \ No newline at end of file diff --git a/PTIRelianceLib/IO/StructuredReader.cs b/PTIRelianceLib/IO/StructuredReader.cs index c6fd251..3738eb4 100644 --- a/PTIRelianceLib/IO/StructuredReader.cs +++ b/PTIRelianceLib/IO/StructuredReader.cs @@ -49,6 +49,8 @@ public IPacket Read(int readLen, params byte[] preamble) break; } + // Extract payload + resp = resp.ExtractPayload(); buffer.AddRange(resp.GetBytes()); ++sequenceNum; } diff --git a/PTIRelianceLib/Internal/FixedArray.cs b/PTIRelianceLib/Internal/FixedArray.cs index c622cc4..c1b0c28 100644 --- a/PTIRelianceLib/Internal/FixedArray.cs +++ b/PTIRelianceLib/Internal/FixedArray.cs @@ -8,11 +8,12 @@ namespace PTIRelianceLib { + using System; using System.Collections.Generic; using System.Collections.Immutable; /// - /// A read-only fixed length array + /// A fixed length array wrapper /// /// Data type to hold public struct FixedArray @@ -62,5 +63,13 @@ public void SetData(params T[] data) /// /// public ImmutableArray GetData() => _mData.ToImmutableArray(); + + /// + /// Returns the element at specified index + /// + /// Index of element to retrieve + /// Returns a reference to object so take care with mutations + /// Object at index + internal T this[int index] => _mData[index]; } } \ No newline at end of file diff --git a/PTIRelianceLib/ReliancePrinter.cs b/PTIRelianceLib/ReliancePrinter.cs index efd67e5..13babf5 100644 --- a/PTIRelianceLib/ReliancePrinter.cs +++ b/PTIRelianceLib/ReliancePrinter.cs @@ -539,6 +539,18 @@ public PowerupTelemetry GetPowerupTelemetry() return GetFirmwareRevision() < new Revlev("1.28") ? null : ReadTelemetry(TelemetryTypes.Powerup); } + /// + /// Reset all telemetry counters. This include the lifetime and powerup counters. + /// This operation cannot be undone. + /// + /// Data will be permanently erased + /// Success code + public ReturnCodes ResetTelemetry() + { + var resp = Write(RelianceCommands.TelemtrySub, 0x03); + return resp.GetPacketType() == PacketTypes.PositiveAck ? ReturnCodes.Okay : ReturnCodes.ExecutionFailure; + } + /// /// Reads the specified telemtry data block /// @@ -594,7 +606,7 @@ internal int ReadTelemetrySize(TelemetryTypes type) return -1; } - var readlen = (int) PacketParserFactory.Instance.Create().Parse(resp).Value; + var readlen = PacketParserFactory.Instance.Create().Parse(resp).Value; if (readlen <= 0) { // Invalid data read length diff --git a/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs b/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs index 2c02003..0ecca66 100644 --- a/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs +++ b/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs @@ -67,7 +67,7 @@ public override LifetimeTelemetry Parse(IPacket packet) } // Ensure there is enough data left in the stream to read - if (packet.Count - 8 <= tel.StructSize) + if (packet.Count < tel.StructSize) { return null; } @@ -87,7 +87,7 @@ public override LifetimeTelemetry Parse(IPacket packet) using (var blockReader = new BinaryReader(blockStream)) { // Read in presented length log, contains 4 byte integers - tel.TicketLengthLog = new FixedArray(rawData.Length); + tel.TicketLengthLog = new FixedArray(PowerupTelemetry.LengthLogLength); for (var i = 0; i < PowerupTelemetry.LengthLogLength; ++i) { tel.TicketLengthLog.SetData(blockReader.ReadInt32()); @@ -101,10 +101,10 @@ public override LifetimeTelemetry Parse(IPacket packet) using (var blockReader = new BinaryReader(blockStream)) { // Read in presented length log, contains 4 byte integers - tel.TicketPresentedLog = new FixedArray(rawData.Length); + tel.TicketPullTimeLog = new FixedArray(PowerupTelemetry.PresentLogLength); for (var i = 0; i < PowerupTelemetry.PresentLogLength; ++i) { - tel.TicketPresentedLog.SetData(blockReader.ReadInt32()); + tel.TicketPullTimeLog.SetData(blockReader.ReadInt32()); } } diff --git a/PTIRelianceLib/Telemetry/PowerupTelemetry.cs b/PTIRelianceLib/Telemetry/PowerupTelemetry.cs index e65e5f5..4bdf650 100644 --- a/PTIRelianceLib/Telemetry/PowerupTelemetry.cs +++ b/PTIRelianceLib/Telemetry/PowerupTelemetry.cs @@ -71,14 +71,14 @@ public class PowerupTelemetry : IParseable /// Each index is a ticket length range. There are 9 /// elements in this array. /// - public FixedArray TicketLengthLog { get; set; } + internal FixedArray TicketLengthLog { get; set; } /// /// Each index is the time a pulled ticket was sitting /// at the bezel. Only pulled tickets are logged. There /// are 11 elements in this array. /// - public FixedArray TicketPresentedLog { get; set; } + internal FixedArray TicketPullTimeLog { get; set; } /// /// Avg time pulled tickets are sitting at the bezel. @@ -125,6 +125,22 @@ public class PowerupTelemetry : IParseable /// public short HighErrorCount { get; set; } + /// + /// Returns the count of tickets within the specified ticket + /// + /// Length group to retrieve + /// Number of tickets printed in specified length group + public int TicketCountsByLength(TicketLengthGroups group) => TicketLengthLog[(int) group]; + + /// + /// Returns the count of tickets that pulled by a customer within the specified time bin. + /// These counts only include tickets pull by the customer. That means that retractions and + /// ejections are not included in this metric. + /// + /// Time group to retrieve + /// Number of tickets in specified time group + public int TicketCountByTimeToPull(TicketPullTimeGroups group) => TicketPullTimeLog[(int) group]; + /// public virtual byte[] Serialize() { diff --git a/PTIRelianceLib/Telemetry/TicketLengthGroups.cs b/PTIRelianceLib/Telemetry/TicketLengthGroups.cs new file mode 100644 index 0000000..a7595b8 --- /dev/null +++ b/PTIRelianceLib/Telemetry/TicketLengthGroups.cs @@ -0,0 +1,55 @@ +#region Header +// TicketLengthGroups.cs +// PTIRelianceLib +// Cory Todd +// 25-06-2018 +// 10:53 AM +#endregion + +namespace PTIRelianceLib.Telemetry +{ + /// + /// Ticket length tracking uses binning to group count of ticket lengths + /// together. There are 9 length groups that are tracked by length + /// in millimeters. + /// + public enum TicketLengthGroups + { + /// + /// Ticket lengths up to 86.375 mm + /// + Bin86 = 0, + /// + /// Ticket lengths from to 118.125 mm + /// + Bin118, + /// + /// Ticket lengths from 118.125 mm to 149.875 mm + /// + Bin149, + /// + /// Ticket lengths from 149.875 mm to 187.875 mm + /// + Bin187, + /// + /// Ticket lengths from 187.875 mm to 226 mm + /// + Bin226, + /// + /// Ticket lengths from 226 mm to 276.875 mm + /// + Bin276, + /// + /// Ticket lengths from 276.875 mm to 327.625 mm + /// + Bin327, + /// + /// Ticket lengths from 327.625 mm to 442 mm + /// + Bin442, + /// + /// Tickets lengths exceeding 442 mm + /// + BinOversized, + } +} \ No newline at end of file diff --git a/PTIRelianceLib/Telemetry/TicketPullTimeGroups.cs b/PTIRelianceLib/Telemetry/TicketPullTimeGroups.cs new file mode 100644 index 0000000..621a8fe --- /dev/null +++ b/PTIRelianceLib/Telemetry/TicketPullTimeGroups.cs @@ -0,0 +1,64 @@ +#region Header +// TicketPullTimeGroups.cs +// PTIRelianceLib +// Cory Todd +// 25-06-2018 +// 11:29 AM +#endregion + +namespace PTIRelianceLib.Telemetry +{ + /// + /// Each printed ticket that sits at the bezel starts a clock. The time between + /// print presented and a customer pulling the ticket is is tracked using binning. + /// The times are tracked in whole seconds. Only tickets that are pulled are counted + /// in these groupings. i.e. Ticket retraction and auto-ejected tickets are not counted. + /// + public enum TicketPullTimeGroups + { + /// + /// Ticket pulled within 4 seconds + /// + Bin4, + /// + /// Ticket pull took more than 4 seconds, up to 8 seconds + /// + Bin8, + /// + /// Ticket pull took more than 8 seconds, up to 16 seconds + /// + Bin16, + /// + /// Ticket pull took more than 16 seconds, up to 32 seconds + /// + Bin32, + /// + /// Ticket pull took more than 32 seconds, up to 50 seconds + /// + Bin50, + /// + /// Ticket pull took more than 50 seconds, up to 70 seconds + /// + Bin70, + /// + /// Ticket pull took more than 70 seconds, up to 90 seconds + /// + Bin90, + /// + /// Ticket pull took more than 90 seconds, up to 120 seconds + /// + Bin120, + /// + /// Ticket pull took more than 120 seconds, up to 180 seconds + /// + Bin180, + /// + /// Ticket pull took more than 180 seconds, up to 300 seconds + /// + Bin300, + /// + /// Ticket pull took more than 300 seconds + /// + BinOvertime + } +} \ No newline at end of file From 251a9de2076472dedad67934a233044a23ebd485 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Mon, 25 Jun 2018 14:36:32 -0700 Subject: [PATCH 17/25] Add raw content string to FixedArray for debugging CLI now pretty prints telemetry as JSON Remove DEBUG flag from release builds Disable unsafe mode for compilation --- .../Internal/ExtensionsTests.cs | 33 +++++++++++++++++++ .../Internal/FixedArrayTests.cs | 2 ++ PTIRelianceLib/Internal/FixedArray.cs | 7 +++- PTIRelianceLib/PTIRelianceLib.csproj | 3 +- PTIRelianceLib/Telemetry/PowerupTelemetry.cs | 18 ++++++++-- RelianceCLI/Program.cs | 8 +++-- 6 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 PTIRelianceLib.Tests/Internal/ExtensionsTests.cs diff --git a/PTIRelianceLib.Tests/Internal/ExtensionsTests.cs b/PTIRelianceLib.Tests/Internal/ExtensionsTests.cs new file mode 100644 index 0000000..4a4e28a --- /dev/null +++ b/PTIRelianceLib.Tests/Internal/ExtensionsTests.cs @@ -0,0 +1,33 @@ +using Xunit; + +namespace PTIRelianceLib.Tests.Internal +{ + using System.Collections.Generic; + + public class ExtensionsTests + { + [Fact] + public void ToUshortBE() + { + var data = new Dictionary + { + {new byte[] {0, 0}, 0}, + {new byte[] {1, 0}, 1}, + {new byte[] {2, 1}, 258}, + {new byte[] {3, 2}, 515}, + {new byte[] {4, 3}, 772}, + {new byte[] {255, 255}, 0xFFFF} + }; + + foreach (var kv in data) + { + var actual1 = kv.Key.ToUshortBE(); + Assert.Equal(kv.Value, actual1); + + // Run the reverse test on the same data + var actual2 = actual1.ToBytesBE(); + Assert.Equal(kv.Key, actual2); + } + } + } +} diff --git a/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs b/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs index 7535f9d..a2149da 100644 --- a/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs +++ b/PTIRelianceLib.Tests/Internal/FixedArrayTests.cs @@ -12,6 +12,7 @@ public void TestCtor() var farr = new FixedArray(10); Assert.Equal(10, farr.Size); Assert.Equal(0, farr.Count); + Assert.True(string.IsNullOrEmpty(farr.Content)); } [Fact] @@ -37,6 +38,7 @@ public void TestSetData() { Assert.Equal(len++, bf.Length); } + Assert.False(string.IsNullOrEmpty(farr.Content)); } [Fact] diff --git a/PTIRelianceLib/Internal/FixedArray.cs b/PTIRelianceLib/Internal/FixedArray.cs index c1b0c28..b92336d 100644 --- a/PTIRelianceLib/Internal/FixedArray.cs +++ b/PTIRelianceLib/Internal/FixedArray.cs @@ -8,7 +8,6 @@ namespace PTIRelianceLib { - using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -40,6 +39,12 @@ public FixedArray(int size) /// public int Count => _mData.Count; + /// + /// Raw raw contents of this array as a comma-separate string using + /// ToString on the objects in this array. + /// + public string Content => string.Join(",", _mData); + /// /// Puts data into this fixed length array starting from the current tail /// of the fixed array. If the fixed array size limit has been reached, diff --git a/PTIRelianceLib/PTIRelianceLib.csproj b/PTIRelianceLib/PTIRelianceLib.csproj index 61c9921..d26ba26 100644 --- a/PTIRelianceLib/PTIRelianceLib.csproj +++ b/PTIRelianceLib/PTIRelianceLib.csproj @@ -23,13 +23,14 @@ 0.3.1.0 true true + false bin\x64\Release\netcoreapp2.0\PTIRelianceLib.xml 0 1701;1702;NU5100 - DEBUG;TRACE;RELEASE;NETCOREAPP;NETCOREAPP2_0;SAFE;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0 + RELEASE;NETCOREAPP;NETCOREAPP2_0;SAFE;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0 diff --git a/PTIRelianceLib/Telemetry/PowerupTelemetry.cs b/PTIRelianceLib/Telemetry/PowerupTelemetry.cs index 4bdf650..f64fbd5 100644 --- a/PTIRelianceLib/Telemetry/PowerupTelemetry.cs +++ b/PTIRelianceLib/Telemetry/PowerupTelemetry.cs @@ -1,9 +1,11 @@ #region Header + // RELTelemetry.cs // PTIRelianceLib // Cory Todd // 19-06-2018 // 7:19 AM + #endregion namespace PTIRelianceLib.Telemetry @@ -71,14 +73,24 @@ public class PowerupTelemetry : IParseable /// Each index is a ticket length range. There are 9 /// elements in this array. /// - internal FixedArray TicketLengthLog { get; set; } +#if DEBUG + public +#else + internal +#endif + FixedArray TicketLengthLog { get; set; } /// /// Each index is the time a pulled ticket was sitting /// at the bezel. Only pulled tickets are logged. There /// are 11 elements in this array. /// - internal FixedArray TicketPullTimeLog { get; set; } +#if DEBUG + public +#else + internal +#endif + FixedArray TicketPullTimeLog { get; set; } /// /// Avg time pulled tickets are sitting at the bezel. @@ -161,7 +173,7 @@ public override PowerupTelemetry Parse(IPacket packet) { var parser = new LifetimeTelemetryParser(); var tel = parser.Parse(packet); - if(tel == null) + if (tel == null) { // Parse failed return null; diff --git a/RelianceCLI/Program.cs b/RelianceCLI/Program.cs index 858dfe6..a362d24 100644 --- a/RelianceCLI/Program.cs +++ b/RelianceCLI/Program.cs @@ -5,6 +5,8 @@ namespace RelianceCLI { using System.IO; + using Newtonsoft.Json; + using Newtonsoft.Json.Converters; using PTIRelianceLib.Imaging; using PTIRelianceLib.Protocol; @@ -147,13 +149,15 @@ private static void Run(Options opts) if (opts.LifetimeTelemetry) { var resp = printer.GetLifetimeTelemetry(); - Console.WriteLine("Total power-on count: {0}", resp?.PowerUpCount); + var str = JsonConvert.SerializeObject(resp, Formatting.Indented, new StringEnumConverter()); + Console.WriteLine("Lifetime Telementry:\n{0}", str); } if (opts.StartupTelemetry) { var resp = printer.GetPowerupTelemetry(); - Console.WriteLine("Tickets printed since last powerup: {0}", resp?.TicketCount); + var str = JsonConvert.SerializeObject(resp, Formatting.Indented, new StringEnumConverter()); + Console.WriteLine("Powerup Telementry:\n{0}", str); } if (opts.GetStatus) From 02baf97d015059b41ac4550cd720fc6cf50730a4 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 26 Jun 2018 11:18:31 -0700 Subject: [PATCH 18/25] Fix project defines in project file Fix two public level comment parameters --- PTIRelianceLib/Logo/RELLogoParser.cs | 1 + PTIRelianceLib/Logo/RELLogoUpdater.cs | 2 +- PTIRelianceLib/PTIRelianceLib.csproj | 6 +++--- RelianceCLI/RelianceCLI.csproj | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/PTIRelianceLib/Logo/RELLogoParser.cs b/PTIRelianceLib/Logo/RELLogoParser.cs index 2e1cd0d..84eb1cb 100644 --- a/PTIRelianceLib/Logo/RELLogoParser.cs +++ b/PTIRelianceLib/Logo/RELLogoParser.cs @@ -40,6 +40,7 @@ internal class RELLogoParser /// /// Parse file as a logo /// + /// Starting storage address /// Logo data to parse /// Packetized list or null on error public IList Parse(uint startAddress, byte[] data) diff --git a/PTIRelianceLib/Logo/RELLogoUpdater.cs b/PTIRelianceLib/Logo/RELLogoUpdater.cs index 368803e..b67ff83 100644 --- a/PTIRelianceLib/Logo/RELLogoUpdater.cs +++ b/PTIRelianceLib/Logo/RELLogoUpdater.cs @@ -26,7 +26,7 @@ internal class RELLogoUpdater : IFlashUpdater /// Constructs a new flasher /// /// Port to write to - /// Data to write + /// Data to write /// Thrown if port or fileToFlash are null public RELLogoUpdater(IPort port, RELLogoHeader header) { diff --git a/PTIRelianceLib/PTIRelianceLib.csproj b/PTIRelianceLib/PTIRelianceLib.csproj index d26ba26..028020c 100644 --- a/PTIRelianceLib/PTIRelianceLib.csproj +++ b/PTIRelianceLib/PTIRelianceLib.csproj @@ -10,7 +10,7 @@ A Reliance Thermal printer API that support firmware updates and configuration. This requires the Reliance printer to be connected via USB. 2018 Pyramid Technologies https://pti.mit-license.org/ - Supports flash updating, confguration read, and configuration write + Supports flash updating, configuration read, and configuration write 0.3.1.0 https://github.com/PyramidTechnologies/PTI.Reliance.Tools https://github.com/PyramidTechnologies/PTI.Reliance.Tools @@ -30,14 +30,14 @@ bin\x64\Release\netcoreapp2.0\PTIRelianceLib.xml 0 1701;1702;NU5100 - RELEASE;NETCOREAPP;NETCOREAPP2_0;SAFE;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0;RELEASE;NETCOREAPP;NETCOREAPP2_0 + SAFE;RELEASE;NETCOREAPP;NETCOREAPP2_0; 1701;1702;NU5100 embedded true - TRACE;DEBUG;NETCOREAPP;NETCOREAPP2_0;NETCOREAPP;NETCOREAPP2_0;NETCOREAPP;NETCOREAPP2_0;SAFE;NETCOREAPP;NETCOREAPP2_0;NETCOREAPP;NETCOREAPP2_0 + SAFE;TRACE;DEBUG;NETCOREAPP;NETCOREAPP2_0 false diff --git a/RelianceCLI/RelianceCLI.csproj b/RelianceCLI/RelianceCLI.csproj index 6447319..9ceae57 100644 --- a/RelianceCLI/RelianceCLI.csproj +++ b/RelianceCLI/RelianceCLI.csproj @@ -6,6 +6,7 @@ AnyCPU;x64 false false + false From 053ad13c8e7746d46971d835d4d07a12a1951c23 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 26 Jun 2018 11:42:46 -0700 Subject: [PATCH 19/25] Add LastTicketState to telemetry object --- PTIRelianceLib/Telemetry/EjectionStatus.cs | 30 ++++++++++++++++++ PTIRelianceLib/Telemetry/LastTicketState.cs | 31 +++++++++++++++++++ PTIRelianceLib/Telemetry/LifetimeTelemetry.cs | 11 +++++++ PTIRelianceLib/Telemetry/PowerupTelemetry.cs | 7 ++++- 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 PTIRelianceLib/Telemetry/EjectionStatus.cs create mode 100644 PTIRelianceLib/Telemetry/LastTicketState.cs diff --git a/PTIRelianceLib/Telemetry/EjectionStatus.cs b/PTIRelianceLib/Telemetry/EjectionStatus.cs new file mode 100644 index 0000000..5275a47 --- /dev/null +++ b/PTIRelianceLib/Telemetry/EjectionStatus.cs @@ -0,0 +1,30 @@ +#region Header +// EjectionStatus.cs +// PTIRelianceLib +// Cory Todd +// 26-06-2018 +// 11:34 AM +#endregion + +namespace PTIRelianceLib.Telemetry +{ + /// + /// A ticket, once printed, terminates in one of two states. On + /// powerup, the floating state is called NoTicket. + /// + public enum EjectionStatus + { + /// + /// No ticket state on record + /// + NoTicket = 0, + /// + /// Last ticket was ejected from bezel + /// + Ejected = 1, + /// + /// Last ticket was retracted + /// + Retracted = 2 + } +} \ No newline at end of file diff --git a/PTIRelianceLib/Telemetry/LastTicketState.cs b/PTIRelianceLib/Telemetry/LastTicketState.cs new file mode 100644 index 0000000..edbea93 --- /dev/null +++ b/PTIRelianceLib/Telemetry/LastTicketState.cs @@ -0,0 +1,31 @@ +#region Header +// LastTicketState.cs +// PTIRelianceLib +// Cory Todd +// 26-06-2018 +// 11:34 AM +#endregion + +namespace PTIRelianceLib.Telemetry +{ + /// + /// Each ticket print is tracked by ejection and length + /// + public struct LastTicketState + { + /// + /// How was the last ticket handled + /// + public EjectionStatus Status { get; set; } + + /// + /// Length of the last ticket in mm + /// + public int LengthMm { get; set; } + + /// + /// Reserved + /// + internal int Reserved { get; set; } + } +} \ No newline at end of file diff --git a/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs b/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs index 0ecca66..386114b 100644 --- a/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs +++ b/PTIRelianceLib/Telemetry/LifetimeTelemetry.cs @@ -118,6 +118,17 @@ public override LifetimeTelemetry Parse(IPacket packet) tel.OverheatedCount = reader.ReadInt32(); tel.CriticalErrorCount = reader.ReadInt32(); tel.HighErrorCount = reader.ReadInt16(); + + // Start rev3 attributes + if (tel.StructRevision >= 3) + { + tel.LastTicketState = new LastTicketState + { + Status = (EjectionStatus) reader.ReadInt32(), + LengthMm = reader.ReadInt32(), + Reserved = reader.ReadInt32(), + }; + } } return tel; diff --git a/PTIRelianceLib/Telemetry/PowerupTelemetry.cs b/PTIRelianceLib/Telemetry/PowerupTelemetry.cs index f64fbd5..f5c4394 100644 --- a/PTIRelianceLib/Telemetry/PowerupTelemetry.cs +++ b/PTIRelianceLib/Telemetry/PowerupTelemetry.cs @@ -135,7 +135,12 @@ public class PowerupTelemetry : IParseable /// /// How many times the printer was in a high priority error. /// - public short HighErrorCount { get; set; } + public short HighErrorCount { get; set; } + + /// + /// State of the last ticket + /// + public LastTicketState LastTicketState { get; set; } /// /// Returns the count of tickets within the specified ticket From 82b3406c45f557572c61db25e62db06be75bd71c Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 26 Jun 2018 12:48:22 -0700 Subject: [PATCH 20/25] Switch to netcore 2.1 This allows us to use the latest Microsoft.Windows.Compatibility library which is required for System.Drawing reference on non-Windows hosts. --- PTIRelianceLib.Tests/PTIRelianceLib.Tests.csproj | 2 +- PTIRelianceLib/PTIRelianceLib.csproj | 4 ++-- RelianceCLI/RelianceCLI.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PTIRelianceLib.Tests/PTIRelianceLib.Tests.csproj b/PTIRelianceLib.Tests/PTIRelianceLib.Tests.csproj index d110733..e2732e5 100644 --- a/PTIRelianceLib.Tests/PTIRelianceLib.Tests.csproj +++ b/PTIRelianceLib.Tests/PTIRelianceLib.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp2.0 + netcoreapp2.1 false diff --git a/PTIRelianceLib/PTIRelianceLib.csproj b/PTIRelianceLib/PTIRelianceLib.csproj index 028020c..e218793 100644 --- a/PTIRelianceLib/PTIRelianceLib.csproj +++ b/PTIRelianceLib/PTIRelianceLib.csproj @@ -2,7 +2,7 @@ PTI.Reliance.Tools - netcoreapp2.0 + netcoreapp2.1 AnyCPU;x64 true Cory Todd <cory@pyramidacceptors.com> @@ -63,8 +63,8 @@ - + diff --git a/RelianceCLI/RelianceCLI.csproj b/RelianceCLI/RelianceCLI.csproj index 9ceae57..70dd705 100644 --- a/RelianceCLI/RelianceCLI.csproj +++ b/RelianceCLI/RelianceCLI.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + netcoreapp2.1 AnyCPU;x64 false false From 4fe5444fcc39053cf7cd6877c643b8cc5e178ac0 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 26 Jun 2018 13:21:44 -0700 Subject: [PATCH 21/25] Default to safe mode, UNSAFE is now explicit --- PTIRelianceLib/Imaging/ImageExt.cs | 41 ++++++++++++++-------------- PTIRelianceLib/PTIRelianceLib.csproj | 4 +-- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/PTIRelianceLib/Imaging/ImageExt.cs b/PTIRelianceLib/Imaging/ImageExt.cs index 0bc5718..880e6c5 100644 --- a/PTIRelianceLib/Imaging/ImageExt.cs +++ b/PTIRelianceLib/Imaging/ImageExt.cs @@ -88,26 +88,8 @@ public static Bitmap AsBitmap(this byte[] imageData, int width, int height) // Timing benchmarks show that safe vs. unsafe has zero impact on our use case // Both safe and unsafe, when tested through logo write, get a 13.2Kbps throughput // This bitmap operation is negligable compared to how slowly the flash data is written. -#if SAFE - // Iterate through rows and columns - for (var row = 0; row < height; row++) - { - // Source is ARGB format but dest is 1bpp so user two different indexers - for (int col = 0, pixCol=0; col < width; col += 4, ++pixCol) - { - var index = row * width + col; - var color = imageData[index++] | - imageData[index++] << 8 | - imageData[index++] << 16 | - imageData[index] << 24; - - // Set pixel - result.SetPixel(pixCol, row, Color.FromArgb(color)); - } - } - -#else - // Lock the entire bitmap +#if UNSAFE +// Lock the entire bitmap var bitmapData = result.LockBits( new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, @@ -137,6 +119,23 @@ public static Bitmap AsBitmap(this byte[] imageData, int width, int height) // Unlock the bitmap result.UnlockBits(bitmapData); +#else + // Iterate through rows and columns + for (var row = 0; row < height; row++) + { + // Source is ARGB format but dest is 1bpp so user two different indexers + for (int col = 0, pixCol = 0; col < width; col += 4, ++pixCol) + { + var index = row * width + col; + var color = imageData[index++] | + imageData[index++] << 8 | + imageData[index++] << 16 | + imageData[index] << 24; + + // Set pixel + result.SetPixel(pixCol, row, Color.FromArgb(color)); + } + } #endif return result; @@ -176,7 +175,7 @@ public static void InvertColorChannels(this Bitmap bitmapImage) Marshal.Copy(bitmapBgra, 0, bmpWo.Scan0, bmpLen); bitmapImage.UnlockBits(bmpWo); } - + /// /// Creates an MSB ordered, bit-reversed buffer of the logo data located in this bitmap. /// The input data's pixels are reduced into an 8bpp image. That means that 8 PC bitmap diff --git a/PTIRelianceLib/PTIRelianceLib.csproj b/PTIRelianceLib/PTIRelianceLib.csproj index e218793..f6fb1a8 100644 --- a/PTIRelianceLib/PTIRelianceLib.csproj +++ b/PTIRelianceLib/PTIRelianceLib.csproj @@ -30,14 +30,14 @@ bin\x64\Release\netcoreapp2.0\PTIRelianceLib.xml 0 1701;1702;NU5100 - SAFE;RELEASE;NETCOREAPP;NETCOREAPP2_0; + RELEASE;NETCOREAPP;NETCOREAPP2_0;;RELEASE;NETCOREAPP;NETCOREAPP2_1 1701;1702;NU5100 embedded true - SAFE;TRACE;DEBUG;NETCOREAPP;NETCOREAPP2_0 + TRACE;DEBUG;NETCOREAPP;NETCOREAPP2_0;NETCOREAPP;NETCOREAPP2_1 false From 6bb5454a9efa188e63f56dd46e2c055061849d4b Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 26 Jun 2018 14:52:47 -0700 Subject: [PATCH 22/25] Add telemetry samples and docs --- docsource/tutorials/Sample_06.cs | 27 +++++++++++++++++++++++++++ docsource/tutorials/telemetry.md | 27 +++++++++++++++++++++++++++ docsource/tutorials/toc.yml | 2 ++ 3 files changed, 56 insertions(+) create mode 100644 docsource/tutorials/Sample_06.cs create mode 100644 docsource/tutorials/telemetry.md diff --git a/docsource/tutorials/Sample_06.cs b/docsource/tutorials/Sample_06.cs new file mode 100644 index 0000000..416044e --- /dev/null +++ b/docsource/tutorials/Sample_06.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using PTIRelianceLib; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace reliance_sample +{ + class Program + { + static void Main(string[] args) + { + // Wrap our printer in using so it gets disposed on properly + using (var printer = new ReliancePrinter()) + { + var tel1 = printer.GetPowerupTelemetry(); + var str = JsonConvert.SerializeObject(tel1, Formatting.Indented, new StringEnumConverter()); + Console.WriteLine("Powerup Telementry:\n{0}", str); + + var tel2 = printer.GetLifetimeTelemetry(); + var str = JsonConvert.SerializeObject(tel2, Formatting.Indented, new StringEnumConverter()); + Console.WriteLine("Lifetime Telementry:\n{0}", str); + } + } + } +} \ No newline at end of file diff --git a/docsource/tutorials/telemetry.md b/docsource/tutorials/telemetry.md new file mode 100644 index 0000000..8de3196 --- /dev/null +++ b/docsource/tutorials/telemetry.md @@ -0,0 +1,27 @@ +# Telemetry +The Reliance Thermal printer tracks the metrics that matter. We have counters for just about any even you can imagine and they are all accessible through this API. With this information, you can track paper consumption, ticket pull habits, error patterns, and many other metrics. + +There are two groups for telemetry: + +1. Lifetime : This is a cumulative record of all events since the printer left our factory. +2. Powerup : This is a record of all events since the last power cycle. + +See and for more details. + +> [!Warning] +> Require Firmware 1.28+. Calling this API on older firmware will return null. + +## Ticket Pull +The best way to detect a ticket pull is request the telemetry information and inspect the property. This records the most recent action taken on a printed ticket along with the ticket's length in millimeters. We recommend that you do not poll the printer more than 4 times a second in order to prevent unecessary blocking read requests. After a ticket is printed, poll the printer about once a second and watch for the TicketCount property to increment. Once it increments, you can read the LastTicketState property and examine how the ticket was handled. + + +## Ticket Lengths +Tickets lengths are tracked by binning ticket lengths into 9 groups. The exact lengths groups are enumerated in in millimeters. The telemetry object tracks the count of each ticket by these groups. All tickets with the exception of startup and push-button diagnostic tickets are counted in this metric. + +## Ticket Pull Time +Tickets pull time is the time in second it took for a customer to pull the ticket from the printer. If a ticket is never pulled and is instead ejected or retracted, no measurement will be taken. The exact time groups are enumerated in in second. + +## Code Sample +[!code-csharp[Main](Sample_06.cs)] + +[!include[]()] \ No newline at end of file diff --git a/docsource/tutorials/toc.yml b/docsource/tutorials/toc.yml index c1fe4aa..50c2033 100644 --- a/docsource/tutorials/toc.yml +++ b/docsource/tutorials/toc.yml @@ -8,6 +8,8 @@ href: logos.md - name: Status Check href: status_check.md +- name: Telemetry + href: telemetry.md - name: Library Logging href: logging.md - name: Library Customization From f3d2700d74b3f49ead76cb0c542fe7badaf87d14 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 26 Jun 2018 14:53:10 -0700 Subject: [PATCH 23/25] Too soon to switch netcore 2.1, reverting to 2.0 --- PTIRelianceLib.Tests/PTIRelianceLib.Tests.csproj | 2 +- PTIRelianceLib/PTIRelianceLib.csproj | 2 +- RelianceCLI/RelianceCLI.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PTIRelianceLib.Tests/PTIRelianceLib.Tests.csproj b/PTIRelianceLib.Tests/PTIRelianceLib.Tests.csproj index e2732e5..d110733 100644 --- a/PTIRelianceLib.Tests/PTIRelianceLib.Tests.csproj +++ b/PTIRelianceLib.Tests/PTIRelianceLib.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp2.1 + netcoreapp2.0 false diff --git a/PTIRelianceLib/PTIRelianceLib.csproj b/PTIRelianceLib/PTIRelianceLib.csproj index f6fb1a8..bb1ca93 100644 --- a/PTIRelianceLib/PTIRelianceLib.csproj +++ b/PTIRelianceLib/PTIRelianceLib.csproj @@ -2,7 +2,7 @@ PTI.Reliance.Tools - netcoreapp2.1 + netcoreapp2.0 AnyCPU;x64 true Cory Todd <cory@pyramidacceptors.com> diff --git a/RelianceCLI/RelianceCLI.csproj b/RelianceCLI/RelianceCLI.csproj index 70dd705..9ceae57 100644 --- a/RelianceCLI/RelianceCLI.csproj +++ b/RelianceCLI/RelianceCLI.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.1 + netcoreapp2.0 AnyCPU;x64 false false From 3c27e9e8a4482e78b2f2300396e86f122bf39f21 Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Tue, 26 Jun 2018 15:03:01 -0700 Subject: [PATCH 24/25] rev'd and pushed to nuget --- PTIRelianceLib/PTIRelianceLib.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PTIRelianceLib/PTIRelianceLib.csproj b/PTIRelianceLib/PTIRelianceLib.csproj index bb1ca93..cad52cc 100644 --- a/PTIRelianceLib/PTIRelianceLib.csproj +++ b/PTIRelianceLib/PTIRelianceLib.csproj @@ -11,16 +11,16 @@ 2018 Pyramid Technologies https://pti.mit-license.org/ Supports flash updating, configuration read, and configuration write - 0.3.1.0 + 0.3.2.0-beta https://github.com/PyramidTechnologies/PTI.Reliance.Tools https://github.com/PyramidTechnologies/PTI.Reliance.Tools git dotnet-core thermal-printer hid https://pyramidacceptors.com/favicon.ico - 0.3.1.0 + 0.3.2.0 true false - 0.3.1.0 + 0.3.2.0 true true false From dc35e34425829dbbdc2eb0a512af24a7347525ad Mon Sep 17 00:00:00 2001 From: Cory Todd Date: Fri, 13 Jul 2018 11:19:42 -0700 Subject: [PATCH 25/25] Promoting to 1.0 status This is the first official, supported release --- PTIRelianceLib/PTIRelianceLib.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PTIRelianceLib/PTIRelianceLib.csproj b/PTIRelianceLib/PTIRelianceLib.csproj index cad52cc..f40b129 100644 --- a/PTIRelianceLib/PTIRelianceLib.csproj +++ b/PTIRelianceLib/PTIRelianceLib.csproj @@ -11,16 +11,16 @@ 2018 Pyramid Technologies https://pti.mit-license.org/ Supports flash updating, configuration read, and configuration write - 0.3.2.0-beta + 1.0 https://github.com/PyramidTechnologies/PTI.Reliance.Tools https://github.com/PyramidTechnologies/PTI.Reliance.Tools git dotnet-core thermal-printer hid https://pyramidacceptors.com/favicon.ico - 0.3.2.0 + 1.0.0.0 true false - 0.3.2.0 + 1.0.0.0 true true false