OSDP.Net 5.2.1
A .NET library for implementing the Open Supervised Device Protocol (OSDP)
Loading...
Searching...
No Matches
ExtendedDeviceIdentification.cs
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
7
9{
15 {
16 private readonly List<ExtendedIdEntry> _entries;
17
22 public ExtendedDeviceIdentification(IEnumerable<ExtendedIdEntry> entries)
23 {
24 _entries = entries?.ToList() ?? new List<ExtendedIdEntry>();
25 }
26
30 public IReadOnlyList<ExtendedIdEntry> Entries => _entries;
31
36 public IEnumerable<ExtendedIdEntry> UnknownEntries => _entries.Where(e => !e.IsKnown);
37
41 public string Manufacturer => GetFirstValue(ExtendedIdTag.Manufacturer);
42
46 public string ProductName => GetFirstValue(ExtendedIdTag.ProductName);
47
51 public string SerialNumber => GetFirstValue(ExtendedIdTag.SerialNumber);
52
56 public IEnumerable<string> FirmwareVersions => GetValues(ExtendedIdTag.FirmwareVersion);
57
61 public string HardwareDescription => GetFirstValue(ExtendedIdTag.HardwareDescription);
62
66 public string Url => GetFirstValue(ExtendedIdTag.Url);
67
71 public string ConfigurationReference => GetFirstValue(ExtendedIdTag.ConfigurationReference);
72
74 public override byte Code => (byte)ReplyType.ExtendedPdIdReport;
75
77 public override ReadOnlySpan<byte> SecurityControlBlock()
78 {
79 return SecurityBlock.ReplyMessageWithDataSecurity;
80 }
81
87 public static ExtendedDeviceIdentification ParseData(ReadOnlySpan<byte> data)
88 {
89 // Check if data starts with a multipart message header (6 bytes minimum)
90 // If so, skip the header and parse only the TLV data
91 ReadOnlySpan<byte> tlvData = data;
92
93 if (data.Length >= 6)
94 {
95 // Check if this looks like a a multi-part message header by validating a structure
96 ushort offset = (ushort)(data[2] | (data[3] << 8));
97 ushort lengthOfFragment = (ushort)(data[4] | (data[5] << 8));
98
99 // If the multipart header is present and valid, skip it
100 if (data.Length == 6 + lengthOfFragment && offset == 0)
101 {
102 tlvData = data.Slice(6);
103 }
104 }
105
106 var entries = new List<ExtendedIdEntry>();
107 int parseOffset = 0;
108
109 while (parseOffset < tlvData.Length)
110 {
111 var entry = ExtendedIdEntry.ParseData(tlvData.Slice(parseOffset), out int bytesConsumed);
112 if (entry == null || bytesConsumed == 0)
113 {
114 if (parseOffset < tlvData.Length)
115 {
116 throw new Exception($"Failed to parse TLV entry at offset {parseOffset}, {tlvData.Length - parseOffset} bytes remaining");
117 }
118 break;
119 }
120
121 entries.Add(entry);
122 parseOffset += bytesConsumed;
123 }
124
125 return new ExtendedDeviceIdentification(entries);
126 }
127
129 public override byte[] BuildData()
130 {
131 // Build the TLV entries
132 var tlvData = new List<byte>();
133 foreach (var entry in _entries)
134 {
135 tlvData.AddRange(entry.BuildData());
136 }
137
138 // Extended ID Report must be sent as a multipart message format:
139 // 2 bytes: Whole Message Length (little-endian)
140 // 2 bytes: Offset (little-endian)
141 // 2 bytes: Length of Fragment (little-endian)
142 // N bytes: Data (the TLV stream)
143 var wholeMessageLength = (ushort)tlvData.Count;
144 var result = new List<byte>
145 {
146 // Whole Message Length (LSB, MSB)
147 (byte)(wholeMessageLength & 0xFF),
148 (byte)((wholeMessageLength >> 8) & 0xFF),
149
150 // Offset (LSB, MSB) - always 0 for single fragment
151 0x00,
152 0x00,
153
154 // Length of Fragment (LSB, MSB) - same as a whole message for a single fragment
155 (byte)(wholeMessageLength & 0xFF),
156 (byte)((wholeMessageLength >> 8) & 0xFF)
157 };
158
159 // Add the TLV data
160 result.AddRange(tlvData);
161
162 return result.ToArray();
163 }
164
166 public override string ToString()
167 {
168 var build = new StringBuilder();
169
170 if (Manufacturer != null)
171 {
172 build.AppendLine($" Manufacturer: {Manufacturer}");
173 }
174
175 if (ProductName != null)
176 {
177 build.AppendLine($" Product Name: {ProductName}");
178 }
179
180 if (SerialNumber != null)
181 {
182 build.AppendLine($" Serial Number: {SerialNumber}");
183 }
184
185 var firmwareVersions = FirmwareVersions.ToList();
186 for (int i = 0; i < firmwareVersions.Count; i++)
187 {
188 var label = i == 0 ? "Firmware Version" : $"Firmware Version {i + 1}";
189 build.AppendLine($"{label,16}: {firmwareVersions[i]}");
190 }
191
192 if (HardwareDescription != null)
193 {
194 build.AppendLine($" Hardware: {HardwareDescription}");
195 }
196
197 if (Url != null)
198 {
199 build.AppendLine($" URL: {Url}");
200 }
201
202 if (ConfigurationReference != null)
203 {
204 build.AppendLine($" Configuration: {ConfigurationReference}");
205 }
206
207 // Display any unknown/undefined tags
208 var unknownEntries = UnknownEntries.ToList();
209 if (unknownEntries.Any())
210 {
211 foreach (var entry in unknownEntries)
212 {
213 build.AppendLine($" Unknown Tag {entry.TagByte}: {entry.Value}");
214 }
215 }
216
217 return build.ToString();
218 }
219
220 private string GetFirstValue(ExtendedIdTag tag)
221 {
222 return _entries.FirstOrDefault(e => e.Tag == tag)?.Value;
223 }
224
225 private IEnumerable<string> GetValues(ExtendedIdTag tag)
226 {
227 return _entries.Where(e => e.Tag == tag).Select(e => e.Value);
228 }
229 }
230}
Contains standard Security Control Block data that can be used when building messages.
Base class representing a payload an OSDP message.
Definition PayloadData.cs:9
string Manufacturer
Gets the manufacturer name, or null if not present.
string HardwareDescription
Gets the hardware description, or null if not present.
IReadOnlyList< ExtendedIdEntry > Entries
Gets all TLV entries in this identification.
string ProductName
Gets the product name, or null if not present.
IEnumerable< ExtendedIdEntry > UnknownEntries
Gets all entries with unknown/undefined tag types.
string ConfigurationReference
Gets the configuration reference, or null if not present.
IEnumerable< string > FirmwareVersions
Gets all firmware version entries.
string SerialNumber
Gets the serial number, or null if not present.
static ExtendedDeviceIdentification ParseData(ReadOnlySpan< byte > data)
Parses the TLV stream from the given data.
ExtendedDeviceIdentification(IEnumerable< ExtendedIdEntry > entries)
Creates a new instance of ExtendedDeviceIdentification with the specified entries.
Represents a single TLV (Tag-Length-Value) entry in the extended device identification response.
static ExtendedIdEntry ParseData(ReadOnlySpan< byte > data, out int bytesConsumed)
Parses a TLV entry from the given data span.
ReplyType
Identifies the type of OSDP protocol reply (PD -> ACU) message.
Definition ReplyType.cs:7
ExtendedIdTag
Defines the tag types used in the extended device identification TLV stream.