Merge remote-tracking branch 'origin/ConnectPro_v1.7' into ConnectPro_v1.8

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Chris Cameron
2020-09-03 12:05:09 -04:00
17 changed files with 757 additions and 435 deletions

View File

@@ -16,7 +16,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Changed ### Changed
- Repeater changed to use configured callbacks instead of a dumb event - Repeater changed to use configured callbacks instead of a dumb event
- Scheduled action callbacks allow a TimeSpan to be returned to delay actions - Scheduled action callbacks allow a TimeSpan to be returned to delay actions
- Workaround for logged XML format exceptions when failing to parse floats
## [13.0.0] - 2020-09-03
### Added
- Added util methods for removing duplicate whitespace in strings
- Added dequeue overload to ScrollQueue
### Changed
- Replaced Crestron Unzip with Yallie Unzip
- Fixed "version" regex for 4-series
- Fixed date parsing error for 4-series
- Split SimplSharpProMono runtime environment into SimplSharpProSever
- Fixed log formatting on 4-series
## [12.1.0] - 2020-07-14 ## [12.1.0] - 2020-07-14
### Added ### Added

View File

@@ -2,7 +2,6 @@
using ICD.Common.Properties; using ICD.Common.Properties;
using NUnit.Framework; using NUnit.Framework;
using ICD.Common.Utils.Collections; using ICD.Common.Utils.Collections;
using ICD.Common.Utils.EventArguments;
namespace ICD.Common.Utils.Tests.Collections namespace ICD.Common.Utils.Tests.Collections
{ {
@@ -12,12 +11,14 @@ namespace ICD.Common.Utils.Tests.Collections
[Test, UsedImplicitly] [Test, UsedImplicitly]
public void MaxSizeTest() public void MaxSizeTest()
{ {
int unused;
ScrollQueue<int> test = new ScrollQueue<int>(5); ScrollQueue<int> test = new ScrollQueue<int>(5);
test.Enqueue(0); test.Enqueue(0, out unused);
test.Enqueue(1); test.Enqueue(1, out unused);
test.Enqueue(2); test.Enqueue(2, out unused);
test.Enqueue(3); test.Enqueue(3, out unused);
test.Enqueue(4); test.Enqueue(4, out unused);
Assert.AreEqual(5, test.Count); Assert.AreEqual(5, test.Count);
@@ -26,7 +27,7 @@ namespace ICD.Common.Utils.Tests.Collections
Assert.AreEqual(3, test.Count); Assert.AreEqual(3, test.Count);
Assert.AreEqual(2, test.Peek()); Assert.AreEqual(2, test.Peek());
test.Enqueue(0); test.Enqueue(0, out unused);
Assert.AreEqual(3, test.Count); Assert.AreEqual(3, test.Count);
Assert.AreEqual(3, test.Peek()); Assert.AreEqual(3, test.Peek());
@@ -35,8 +36,10 @@ namespace ICD.Common.Utils.Tests.Collections
[Test, UsedImplicitly] [Test, UsedImplicitly]
public void ClearTest() public void ClearTest()
{ {
int unused;
ScrollQueue<int> test = new ScrollQueue<int>(5); ScrollQueue<int> test = new ScrollQueue<int>(5);
test.Enqueue(0); test.Enqueue(0, out unused);
test.Clear(); test.Clear();
Assert.AreEqual(0, test.Count); Assert.AreEqual(0, test.Count);
@@ -45,9 +48,11 @@ namespace ICD.Common.Utils.Tests.Collections
[Test, UsedImplicitly] [Test, UsedImplicitly]
public void EnqueueTest() public void EnqueueTest()
{ {
int unused;
ScrollQueue<int> test = new ScrollQueue<int>(5); ScrollQueue<int> test = new ScrollQueue<int>(5);
test.Enqueue(0); test.Enqueue(0, out unused);
test.Enqueue(1); test.Enqueue(1, out unused);
Assert.AreEqual(2, test.Count); Assert.AreEqual(2, test.Count);
@@ -60,9 +65,11 @@ namespace ICD.Common.Utils.Tests.Collections
[Test, UsedImplicitly] [Test, UsedImplicitly]
public void DequeueTest() public void DequeueTest()
{ {
int unused;
ScrollQueue<int> test = new ScrollQueue<int>(5); ScrollQueue<int> test = new ScrollQueue<int>(5);
test.Enqueue(0); test.Enqueue(0, out unused);
test.Enqueue(1); test.Enqueue(1, out unused);
Assert.AreEqual(0, test.Dequeue()); Assert.AreEqual(0, test.Dequeue());
Assert.AreEqual(1, test.Count); Assert.AreEqual(1, test.Count);
@@ -71,49 +78,13 @@ namespace ICD.Common.Utils.Tests.Collections
[Test, UsedImplicitly] [Test, UsedImplicitly]
public void PeekTest() public void PeekTest()
{ {
int unused;
ScrollQueue<int> test = new ScrollQueue<int>(5); ScrollQueue<int> test = new ScrollQueue<int>(5);
test.Enqueue(0); test.Enqueue(0, out unused);
test.Enqueue(1); test.Enqueue(1, out unused);
Assert.AreEqual(0, test.Peek()); Assert.AreEqual(0, test.Peek());
} }
[Test, UsedImplicitly]
public void OnItemTrimmedTest()
{
ScrollQueue<int> test = new ScrollQueue<int>(3);
int? removedItem = null;
test.OnItemTrimmed += (sender, args) => removedItem = args.Data;
test.Enqueue(1);
test.Enqueue(2);
test.Enqueue(3);
Assert.IsNull(removedItem, "Raised Early");
test.Enqueue(4);
Assert.True(removedItem.HasValue, "Not Raised");
if (removedItem.HasValue)
Assert.AreEqual(1, removedItem.Value, "Incorrect Value");
removedItem = null;
test.Enqueue(5);
Assert.True(removedItem.HasValue, "Not Raised");
if (removedItem.HasValue)
Assert.AreEqual(2, removedItem.Value, "Incorrect Value");
removedItem = null;
test.MaxSize = 4;
test.Enqueue(6);
Assert.False(removedItem.HasValue, "Raised Early");
}
} }
} }

View File

@@ -101,6 +101,24 @@ namespace ICD.Common.Utils.Collections
return output; return output;
} }
/// <summary>
/// Dequeues the next item in the queue. Returns false if the queue is empty.
/// </summary>
/// <typeparam name="TContents"></typeparam>
/// <param name="item"></param>
/// <returns></returns>
[PublicAPI]
public bool Dequeue(out TContents item)
{
item = default(TContents);
if (Count == 0)
return false;
item = Dequeue();
return true;
}
/// <summary> /// <summary>
/// Returns the oldest item in the queue. /// Returns the oldest item in the queue.
/// </summary> /// </summary>

View File

@@ -157,6 +157,21 @@ namespace ICD.Common.Utils.Extensions
return new string(extends.Where(c => !char.IsWhiteSpace(c)).ToArray()); return new string(extends.Where(c => !char.IsWhiteSpace(c)).ToArray());
} }
/// <summary>
/// Replaces spans of whitespace with a single space.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
[PublicAPI]
[NotNull]
public static string RemoveDuplicateWhitespace([NotNull] this string extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return Regex.Replace(extends, @"\s+", " ");
}
/// <summary> /// <summary>
/// Removes all occurrences of the given string. /// Removes all occurrences of the given string.
/// </summary> /// </summary>

View File

@@ -127,7 +127,6 @@
<Compile Include="Globalization\IcdCultureInfo.cs" /> <Compile Include="Globalization\IcdCultureInfo.cs" />
<Compile Include="GuidUtils.cs" /> <Compile Include="GuidUtils.cs" />
<Compile Include="IcdUriBuilder.cs" /> <Compile Include="IcdUriBuilder.cs" />
<Compile Include="IO\Compression\IcdZipEntry.cs" />
<Compile Include="IO\eIcdFileMode.cs" /> <Compile Include="IO\eIcdFileMode.cs" />
<Compile Include="IO\IcdBinaryReader.cs" /> <Compile Include="IO\IcdBinaryReader.cs" />
<Compile Include="IO\eSeekOrigin.cs" /> <Compile Include="IO\eSeekOrigin.cs" />
@@ -166,6 +165,7 @@
<Compile Include="IcdEnvironment.cs" /> <Compile Include="IcdEnvironment.cs" />
<Compile Include="IcdEnvironment.SimplSharp.cs" /> <Compile Include="IcdEnvironment.SimplSharp.cs" />
<Compile Include="IcdEnvironment.Standard.cs" /> <Compile Include="IcdEnvironment.Standard.cs" />
<Compile Include="IO\Compression\Unzip.cs" />
<Compile Include="IO\Compression\IcdZip.cs" /> <Compile Include="IO\Compression\IcdZip.cs" />
<Compile Include="IO\IcdDirectory.cs" /> <Compile Include="IO\IcdDirectory.cs" />
<Compile Include="EnumUtils.cs" /> <Compile Include="EnumUtils.cs" />

View File

@@ -1,12 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
using System.IO.Compression;
#endif
namespace ICD.Common.Utils.IO.Compression namespace ICD.Common.Utils.IO.Compression
{ {
@@ -15,9 +8,6 @@ namespace ICD.Common.Utils.IO.Compression
/// </summary> /// </summary>
public static class IcdZip public static class IcdZip
{ {
private const int DIRECTORY_SIGNATURE = 0x06054B50;
private const int ENTRY_SIGNATURE = 0x02014B50;
/// <summary> /// <summary>
/// Unzips the archive at the given path. /// Unzips the archive at the given path.
/// </summary> /// </summary>
@@ -25,14 +15,8 @@ namespace ICD.Common.Utils.IO.Compression
/// <param name="outputPath"></param> /// <param name="outputPath"></param>
public static void Unzip(string path, string outputPath) public static void Unzip(string path, string outputPath)
{ {
#if SIMPLSHARP using (Unzip unzip = new Unzip(path))
CrestronZIP.ResultCode result = CrestronZIP.Unzip(path, outputPath); unzip.ExtractToDirectory(outputPath);
if (result != CrestronZIP.ResultCode.ZR_OK)
throw new InvalidOperationException(result.ToString());
#else
using (ZipArchive archive = ZipFile.Open(path, ZipArchiveMode.Read))
archive.ExtractToDirectory(outputPath);
#endif
} }
/// <summary> /// <summary>
@@ -40,134 +24,8 @@ namespace ICD.Common.Utils.IO.Compression
/// </summary> /// </summary>
public static IEnumerable<string> GetFileNames(string path) public static IEnumerable<string> GetFileNames(string path)
{ {
return GetEntries(path).Select(e => e.Name) using (Unzip unzip = new Unzip(path))
.Where(f => !f.EndsWith("/")) return unzip.FileNames.ToArray();
.OrderBy(f => f);
}
/// <summary>
/// Gets sequence of zip file entries in the archive at the given path.
/// </summary>
public static IEnumerable<IcdZipEntry> GetEntries(string path)
{
/*
Copyright (c) 2012-2013 Alexey Yakovlev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
using (IcdStream stream = IcdFile.OpenRead(path))
{
using (IcdBinaryReader reader = new IcdBinaryReader(stream))
{
if (stream.Length < 22)
yield break;
stream.Seek(-22, eSeekOrigin.End);
// find directory signature
while (reader.ReadInt32() != DIRECTORY_SIGNATURE)
{
if (stream.Position <= 5)
yield break;
// move 1 byte back
stream.Seek(-5, eSeekOrigin.Current);
}
// read directory properties
stream.Seek(6, eSeekOrigin.Current);
ushort entries = reader.ReadUInt16();
int difSize = reader.ReadInt32();
uint dirOffset = reader.ReadUInt32();
stream.Seek(dirOffset, eSeekOrigin.Begin);
// read directory entries
for (int i = 0; i < entries; i++)
{
if (reader.ReadInt32() != ENTRY_SIGNATURE)
continue;
// read file properties
reader.ReadInt32();
bool utf8 = (reader.ReadInt16() & 0x0800) != 0;
short method = reader.ReadInt16();
int timestamp = reader.ReadInt32();
uint crc32 = reader.ReadUInt32();
int compressedSize = reader.ReadInt32();
int fileSize = reader.ReadInt32();
short fileNameSize = reader.ReadInt16();
short extraSize = reader.ReadInt16();
short commentSize = reader.ReadInt16();
int headerOffset = reader.ReadInt32();
reader.ReadInt32();
int fileHeaderOffset = reader.ReadInt32();
byte[] fileNameBytes = reader.ReadBytes(fileNameSize);
stream.Seek(extraSize, eSeekOrigin.Current);
byte[] fileCommentBytes = reader.ReadBytes(commentSize);
int fileDataOffset = CalculateFileDataOffset(stream, reader, fileHeaderOffset);
// decode zip file entry
Encoding encoder = utf8 ? Encoding.UTF8 : Encoding.Default;
yield return new IcdZipEntry
{
Name = encoder.GetString(fileNameBytes, 0, fileNameBytes.Length),
Comment = encoder.GetString(fileCommentBytes, 0, fileCommentBytes.Length),
Crc32 = crc32,
CompressedSize = compressedSize,
OriginalSize = fileSize,
HeaderOffset = fileHeaderOffset,
DataOffset = fileDataOffset,
Deflated = method == 8,
Timestamp = ConvertToDateTime(timestamp)
};
}
}
}
}
private static int CalculateFileDataOffset(IcdStream stream, IcdBinaryReader reader, int fileHeaderOffset)
{
long position = stream.Position;
stream.Seek(fileHeaderOffset + 26, eSeekOrigin.Begin);
short fileNameSize = reader.ReadInt16();
short extraSize = reader.ReadInt16();
int fileOffset = (int)stream.Position + fileNameSize + extraSize;
stream.Seek(position, eSeekOrigin.Begin);
return fileOffset;
}
/// <summary>
/// Converts DOS timestamp to a <see cref="DateTime"/> instance.
/// </summary>
/// <param name="dosTimestamp">The DOS timestamp.</param>
/// <returns>The <see cref="DateTime"/> instance.</returns>
private static DateTime ConvertToDateTime(int dosTimestamp)
{
return new DateTime((dosTimestamp >> 25) + 1980,
(dosTimestamp >> 21) & 15,
(dosTimestamp >> 16) & 31,
(dosTimestamp >> 11) & 31,
(dosTimestamp >> 5) & 63,
(dosTimestamp & 31) * 2);
} }
} }
} }

View File

@@ -1,59 +0,0 @@
using System;
namespace ICD.Common.Utils.IO.Compression
{
/// <summary>
/// Zip archive entry.
/// </summary>
public sealed class IcdZipEntry
{
/// <summary>
/// Gets or sets the name of a file or a directory.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the comment.
/// </summary>
public string Comment { get; set; }
/// <summary>
/// Gets or sets the CRC32.
/// </summary>
public uint Crc32 { get; set; }
/// <summary>
/// Gets or sets the compressed size of the file.
/// </summary>
public int CompressedSize { get; set; }
/// <summary>
/// Gets or sets the original size of the file.
/// </summary>
public int OriginalSize { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="IcdZipEntry" /> is deflated.
/// </summary>
public bool Deflated { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="IcdZipEntry" /> is a directory.
/// </summary>
public bool IsDirectory { get { return Name.EndsWith("/"); } }
/// <summary>
/// Gets or sets the timestamp.
/// </summary>
public DateTime Timestamp { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="IcdZipEntry" /> is a file.
/// </summary>
public bool IsFile { get { return !IsDirectory; } }
public int HeaderOffset { get; set; }
public int DataOffset { get; set; }
}
}

View File

@@ -0,0 +1,458 @@
/*
Copyright (c) 2012-2013 Alexey Yakovlev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.CrestronIO.Compression;
#else
using System.IO;
using System.IO.Compression;
#endif
namespace ICD.Common.Utils.IO.Compression
{
/// <summary>
/// Unzip helper class.
/// </summary>
public sealed class Unzip : IDisposable
{
/// <summary>
/// Zip archive entry.
/// </summary>
public sealed class Entry
{
/// <summary>
/// Gets or sets the name of a file or a directory.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Gets or sets the comment.
/// </summary>
public string Comment { get; set; }
/// <summary>
/// Gets or sets the CRC32.
/// </summary>
public uint Crc32 { get; set; }
/// <summary>
/// Gets or sets the compressed size of the file.
/// </summary>
public int CompressedSize { get; set; }
/// <summary>
/// Gets or sets the original size of the file.
/// </summary>
public int OriginalSize { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="Entry" /> is deflated.
/// </summary>
public bool Deflated { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="Entry" /> is a directory.
/// </summary>
public bool IsDirectory { get { return Name.EndsWith("/"); } }
/// <summary>
/// Gets or sets the timestamp.
/// </summary>
public DateTime Timestamp { get; set; }
/// <summary>
/// Gets a value indicating whether this <see cref="Entry" /> is a file.
/// </summary>
public bool IsFile { get { return !IsDirectory; } }
public int HeaderOffset { get; set; }
public int DataOffset { get; set; }
}
/// <summary>
/// CRC32 calculation helper.
/// </summary>
private sealed class Crc32Calculator
{
private static readonly uint[] s_Crc32Table =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};
private uint m_CrcValue = 0xffffffff;
public uint Crc32 { get { return m_CrcValue ^ 0xffffffff; } }
public void UpdateWithBlock(byte[] buffer, int numberOfBytes)
{
for (var i = 0; i < numberOfBytes; i++)
m_CrcValue = (m_CrcValue >> 8) ^ s_Crc32Table[buffer[i] ^ m_CrcValue & 0xff];
}
}
/// <summary>
/// Provides data for the ExtractProgress event.
/// </summary>
public sealed class FileProgressEventArgs : EventArgs
{
/// <summary>
/// Gets the current file.
/// </summary>
public int CurrentFile { get; private set; }
/// <summary>
/// Gets the total files.
/// </summary>
public int TotalFiles { get; private set; }
/// <summary>
/// Gets the name of the file.
/// </summary>
public string FileName { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="FileProgressEventArgs"/> class.
/// </summary>
/// <param name="currentFile">The current file.</param>
/// <param name="totalFiles">The total files.</param>
/// <param name="fileName">Name of the file.</param>
public FileProgressEventArgs(int currentFile, int totalFiles, string fileName)
{
CurrentFile = currentFile;
TotalFiles = totalFiles;
FileName = fileName;
}
}
private const int ENTRY_SIGNATURE = 0x02014B50;
private const int FILE_SIGNATURE = 0x04034b50;
private const int DIRECTORY_SIGNATURE = 0x06054B50;
private const int BUFFER_SIZE = 16 * 1024;
/// <summary>
/// Occurs when a file or a directory is extracted from an archive.
/// </summary>
public event EventHandler<FileProgressEventArgs> OnExtractProgress;
private Entry[] m_Entries;
private Stream Stream { get; set; }
private BinaryReader Reader { get; set; }
/// <summary>
/// Gets the file names.
/// </summary>
public IEnumerable<string> FileNames
{
get { return Entries.Select(e => e.Name).Where(f => !f.EndsWith("/")).OrderBy(f => f); }
}
/// <summary>
/// Gets zip file entries.
/// </summary>
public Entry[] Entries { get { return m_Entries ?? (m_Entries = ReadZipEntries().ToArray()); } }
/// <summary>
/// Initializes a new instance of the <see cref="Unzip" /> class.
/// </summary>
/// <param name="fileName">Name of the file.</param>
public Unzip(string fileName)
: this(File.OpenRead(fileName))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Unzip" /> class.
/// </summary>
/// <param name="stream">The stream.</param>
public Unzip(Stream stream)
{
Stream = stream;
Reader = new BinaryReader(Stream);
}
/// <summary>
/// Performs application-defined tasks associated with
/// freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (Stream != null)
{
Stream.Dispose();
Stream = null;
}
if (Reader != null)
{
Reader.Close();
Reader = null;
}
}
/// <summary>
/// Extracts the contents of the zip file to the given directory.
/// </summary>
/// <param name="directoryName">Name of the directory.</param>
public void ExtractToDirectory(string directoryName)
{
for (int index = 0; index < Entries.Length; index++)
{
var entry = Entries[index];
// create target directory for the file
var fileName = Path.Combine(directoryName, entry.Name);
var dirName = Path.GetDirectoryName(fileName);
Directory.CreateDirectory(dirName);
// save file if it is not only a directory
if (!entry.IsDirectory)
Extract(entry.Name, fileName);
var extractProgress = OnExtractProgress;
if (extractProgress != null)
extractProgress(this, new FileProgressEventArgs(index + 1, Entries.Length, entry.Name));
}
}
/// <summary>
/// Extracts the specified file to the specified name.
/// </summary>
/// <param name="fileName">Name of the file in zip archive.</param>
/// <param name="outputFileName">Name of the output file.</param>
public void Extract(string fileName, string outputFileName)
{
var entry = GetEntry(fileName);
using (var outStream = File.Create(outputFileName))
Extract(entry, outStream);
var fileInfo = new FileInfo(outputFileName);
if (fileInfo.Length != entry.OriginalSize)
{
throw new FormatException(string.Format(
"Corrupted archive: {0} has an uncompressed size {1} which does not match its expected size {2}",
outputFileName, fileInfo.Length, entry.OriginalSize));
}
#if !SIMPLSHARP
File.SetLastWriteTime(outputFileName, entry.Timestamp);
#endif
}
private Entry GetEntry(string fileName)
{
fileName = fileName.Replace("\\", "/").Trim().TrimStart('/');
var entry = Entries.FirstOrDefault(e => e.Name == fileName);
if (entry == null)
throw new FileNotFoundException("File not found in the archive: " + fileName);
return entry;
}
/// <summary>
/// Extracts the specified file to the output <see cref="Stream"/>.
/// </summary>
/// <param name="fileName">Name of the file in zip archive.</param>
/// <param name="outputStream">The output stream.</param>
public void Extract(string fileName, Stream outputStream)
{
Extract(GetEntry(fileName), outputStream);
}
/// <summary>
/// Extracts the specified entry.
/// </summary>
/// <param name="entry">Zip file entry to extract.</param>
/// <param name="outputStream">The stream to write the data to.</param>
/// <exception cref="System.InvalidOperationException"> is thrown when the file header signature doesn't match.</exception>
public void Extract(Entry entry, Stream outputStream)
{
// check file signature
Stream.Seek(entry.HeaderOffset, SeekOrigin.Begin);
if (Reader.ReadInt32() != FILE_SIGNATURE)
throw new FormatException("File signature doesn't match.");
// move to file data
Stream.Seek(entry.DataOffset, SeekOrigin.Begin);
var inputStream = Stream;
if (entry.Deflated)
inputStream = new DeflateStream(Stream, CompressionMode.Decompress, true);
// allocate buffer, prepare for CRC32 calculation
var count = entry.OriginalSize;
var bufferSize = Math.Min(BUFFER_SIZE, entry.OriginalSize);
var buffer = new byte[bufferSize];
var crc32Calculator = new Crc32Calculator();
while (count > 0)
{
// decompress data
var read = inputStream.Read(buffer, 0, bufferSize);
if (read == 0)
break;
crc32Calculator.UpdateWithBlock(buffer, read);
// copy to the output stream
outputStream.Write(buffer, 0, read);
count -= read;
}
if (crc32Calculator.Crc32 != entry.Crc32)
{
throw new FormatException(string.Format(
"Corrupted archive: CRC32 doesn't match on file {0}: expected {1:x8}, got {2:x8}.",
entry.Name, entry.Crc32, crc32Calculator.Crc32));
}
}
private IEnumerable<Entry> ReadZipEntries()
{
if (Stream.Length < 22)
yield break;
Stream.Seek(-22, SeekOrigin.End);
// find directory signature
while (Reader.ReadInt32() != DIRECTORY_SIGNATURE)
{
if (Stream.Position <= 5)
yield break;
// move 1 byte back
Stream.Seek(-5, SeekOrigin.Current);
}
// read directory properties
Stream.Seek(6, SeekOrigin.Current);
var entries = Reader.ReadUInt16();
var difSize = Reader.ReadInt32();
var dirOffset = Reader.ReadUInt32();
Stream.Seek(dirOffset, SeekOrigin.Begin);
// read directory entries
for (int i = 0; i < entries; i++)
{
if (Reader.ReadInt32() != ENTRY_SIGNATURE)
continue;
// read file properties
// TODO: Replace with a proper class to make this method a lot shorter.
Reader.ReadInt32();
bool utf8 = (Reader.ReadInt16() & 0x0800) != 0;
short method = Reader.ReadInt16();
int timestamp = Reader.ReadInt32();
uint crc32 = Reader.ReadUInt32();
int compressedSize = Reader.ReadInt32();
int fileSize = Reader.ReadInt32();
short fileNameSize = Reader.ReadInt16();
short extraSize = Reader.ReadInt16();
short commentSize = Reader.ReadInt16();
int headerOffset = Reader.ReadInt32();
Reader.ReadInt32();
int fileHeaderOffset = Reader.ReadInt32();
var fileNameBytes = Reader.ReadBytes(fileNameSize);
Stream.Seek(extraSize, SeekOrigin.Current);
var fileCommentBytes = Reader.ReadBytes(commentSize);
var fileDataOffset = CalculateFileDataOffset(fileHeaderOffset);
// decode zip file entry
var encoder = utf8 ? Encoding.UTF8 : Encoding.Default;
yield return new Entry
{
Name = encoder.GetString(fileNameBytes, 0, fileNameBytes.Length),
Comment = encoder.GetString(fileCommentBytes, 0, fileCommentBytes.Length),
Crc32 = crc32,
CompressedSize = compressedSize,
OriginalSize = fileSize,
HeaderOffset = fileHeaderOffset,
DataOffset = fileDataOffset,
Deflated = method == 8,
Timestamp = ConvertToDateTime(timestamp)
};
}
}
private int CalculateFileDataOffset(int fileHeaderOffset)
{
var position = Stream.Position;
Stream.Seek(fileHeaderOffset + 26, SeekOrigin.Begin);
var fileNameSize = Reader.ReadInt16();
var extraSize = Reader.ReadInt16();
var fileOffset = (int)Stream.Position + fileNameSize + extraSize;
Stream.Seek(position, SeekOrigin.Begin);
return fileOffset;
}
/// <summary>
/// Converts DOS timestamp to a <see cref="DateTime"/> instance.
/// </summary>
/// <param name="dosTimestamp">The dos timestamp.</param>
/// <returns>The <see cref="DateTime"/> instance.</returns>
public static DateTime ConvertToDateTime(int dosTimestamp)
{
return new DateTime((dosTimestamp >> 25) + 1980, (dosTimestamp >> 21) & 15, (dosTimestamp >> 16) & 31,
(dosTimestamp >> 11) & 31, (dosTimestamp >> 5) & 63, (dosTimestamp & 31) * 2);
}
}
}

View File

@@ -65,7 +65,7 @@ namespace ICD.Common.Utils
public static void PrintLine(string message) public static void PrintLine(string message)
{ {
#if SIMPLSHARP #if SIMPLSHARP
if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono) if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer)
CrestronConsole.PrintLine(message); CrestronConsole.PrintLine(message);
#else #else
Console.WriteLine(message); Console.WriteLine(message);
@@ -94,7 +94,7 @@ namespace ICD.Common.Utils
public static void Print(string message) public static void Print(string message)
{ {
#if SIMPLSHARP #if SIMPLSHARP
if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono) if (IcdEnvironment.RuntimeEnvironment != IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer)
CrestronConsole.Print(message); CrestronConsole.Print(message);
#else #else
Console.Write(message); Console.Write(message);
@@ -124,7 +124,7 @@ namespace ICD.Common.Utils
{ {
#if SIMPLSHARP #if SIMPLSHARP
// No console on VC4 // No console on VC4
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono) if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer)
return false; return false;
return CrestronConsole.SendControlSystemCommand(command, ref result); return CrestronConsole.SendControlSystemCommand(command, ref result);

View File

@@ -14,19 +14,13 @@ namespace ICD.Common.Utils
/// </summary> /// </summary>
private const string INVALID_VALUE = "Invalid Value"; private const string INVALID_VALUE = "Invalid Value";
private static eRuntimeEnvironment s_RuntimeEnvironment;
#region Properties #region Properties
public static string NewLine { get { return CrestronEnvironment.NewLine; } } public static string NewLine { get { return CrestronEnvironment.NewLine; } }
public static eRuntimeEnvironment RuntimeEnvironment public static eRuntimeEnvironment RuntimeEnvironment { get { return s_RuntimeEnvironment; } }
{
get
{
return CrestronEnvironment.DevicePlatform == eDevicePlatform.Server
? eRuntimeEnvironment.SimplSharpProMono
: GetRuntimeEnvironment(CrestronEnvironment.RuntimeEnvironment);
}
}
/// <summary> /// <summary>
/// Gets the network address(es) of the processor. /// Gets the network address(es) of the processor.
@@ -202,6 +196,14 @@ namespace ICD.Common.Utils
/// </summary> /// </summary>
static IcdEnvironment() static IcdEnvironment()
{ {
// Cache the runtime environment
s_RuntimeEnvironment =
CrestronEnvironment.DevicePlatform == eDevicePlatform.Server
? eRuntimeEnvironment.SimplSharpProServer
: Type.GetType("Mono.Runtime") == null
? GetRuntimeEnvironment(CrestronEnvironment.RuntimeEnvironment)
: eRuntimeEnvironment.SimplSharpProMono;
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironmentOnProgramStatusEventHandler; CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironmentOnProgramStatusEventHandler;
CrestronEnvironment.EthernetEventHandler += CrestronEnvironmentOnEthernetEventHandler; CrestronEnvironment.EthernetEventHandler += CrestronEnvironmentOnEthernetEventHandler;
} }
@@ -258,7 +260,7 @@ namespace ICD.Common.Utils
} }
} }
public static eRuntimeEnvironment GetRuntimeEnvironment(Crestron.SimplSharp.eRuntimeEnvironment runtimeEnvironment) private static eRuntimeEnvironment GetRuntimeEnvironment(Crestron.SimplSharp.eRuntimeEnvironment runtimeEnvironment)
{ {
switch (runtimeEnvironment) switch (runtimeEnvironment)
{ {

View File

@@ -14,6 +14,7 @@ namespace ICD.Common.Utils
SimplSharp, SimplSharp,
SimplSharpPro, SimplSharpPro,
SimplSharpProMono, SimplSharpProMono,
SimplSharpProServer,
Standard Standard
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using ICD.Common.Properties; using ICD.Common.Properties;
#if SIMPLSHARP #if SIMPLSHARP
using Crestron.SimplSharp; using Crestron.SimplSharp;
@@ -8,183 +9,184 @@ namespace ICD.Common.Utils
{ {
public static class IcdErrorLog public static class IcdErrorLog
{ {
private static readonly SafeCriticalSection s_LoggingSection; private const string ERROR = "Error";
private const string WARN = "Warn";
private const string NOTICE = "Notice";
private const string OK = "OK";
private const string EXCEPTION = "Except";
private const string INFO = "Info";
private static readonly Dictionary<string, eConsoleColor> s_SeverityToColor =
new Dictionary<string, eConsoleColor>
{
{ERROR, eConsoleColor.Red},
{WARN, eConsoleColor.Yellow},
{NOTICE, eConsoleColor.Blue},
{OK, eConsoleColor.Green},
{EXCEPTION, eConsoleColor.Red},
{INFO, eConsoleColor.Cyan}
};
private static readonly Dictionary<string, Action<string, Exception>> s_LogMethods =
new Dictionary<string, Action<string, Exception>>
{
#if SIMPLSHARP
{ERROR, (m, e) => ErrorLog.Error(m)},
{WARN, (m, e) => ErrorLog.Warn(m)},
{NOTICE, (m, e) => ErrorLog.Notice(m)},
{OK, (m, e) => ErrorLog.Ok(m)},
{EXCEPTION, ErrorLog.Exception},
{INFO, (m, e) => ErrorLog.Info(m)}
#else
{ERROR, (m, e) => Console.Error.WriteLine(m)},
{WARN, (m, e) => Console.Error.WriteLine(m)},
{NOTICE, (m, e) => Console.Error.WriteLine(m)},
{OK, (m, e) => Console.Error.WriteLine(m)},
{EXCEPTION, (m, e) => Console.Error.WriteLine(m)},
{INFO, (m, e) => Console.Error.WriteLine(m)}
#endif
};
private static readonly SafeCriticalSection s_LoggingSection = new SafeCriticalSection();
#region Methods
/// <summary> /// <summary>
/// Static constructor. /// Logs the error to the standard error output.
/// </summary> /// </summary>
static IcdErrorLog() /// <param name="message"></param>
{ /// <param name="args"></param>
s_LoggingSection = new SafeCriticalSection();
}
[PublicAPI]
public static void Error(string message)
{
s_LoggingSection.Enter();
try
{
message = eConsoleColor.Red.FormatAnsi(message);
#if SIMPLSHARP
ErrorLog.Error(message);
#else
Console.Error.WriteLine("Error - {0} - {1}", IcdEnvironment.GetLocalTime(), message);
#endif
}
finally
{
s_LoggingSection.Leave();
}
}
[PublicAPI] [PublicAPI]
public static void Error(string message, params object[] args) public static void Error(string message, params object[] args)
{ {
Error(string.Format(message, args)); Log(ERROR, message, null, args);
}
[PublicAPI]
public static void Warn(string message)
{
s_LoggingSection.Enter();
try
{
message = eConsoleColor.Yellow.FormatAnsi(message);
#if SIMPLSHARP
ErrorLog.Warn(message);
#else
Console.Error.WriteLine("Warn - {0} - {1}", IcdEnvironment.GetLocalTime(), message);
#endif
}
finally
{
s_LoggingSection.Leave();
}
} }
/// <summary>
/// Logs the warning to the standard error output.
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
[PublicAPI] [PublicAPI]
public static void Warn(string message, params object[] args) public static void Warn(string message, params object[] args)
{ {
Warn(string.Format(message, args)); Log(WARN, message, null, args);
}
[PublicAPI]
public static void Notice(string message)
{
s_LoggingSection.Enter();
try
{
message = eConsoleColor.Blue.FormatAnsi(message);
#if SIMPLSHARP
ErrorLog.Notice(message);
#else
Console.Error.WriteLine("Notice - {0} - {1}", IcdEnvironment.GetLocalTime(), message);
#endif
}
finally
{
s_LoggingSection.Leave();
}
} }
/// <summary>
/// Logs the notice to the standard error output.
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
[PublicAPI] [PublicAPI]
public static void Notice(string message, params object[] args) public static void Notice(string message, params object[] args)
{ {
Notice(string.Format(message, args)); Log(NOTICE, message, null, args);
}
[PublicAPI]
public static void Ok(string message)
{
s_LoggingSection.Enter();
try
{
message = eConsoleColor.Green.FormatAnsi(message);
#if SIMPLSHARP
ErrorLog.Ok(message);
#else
Console.Error.WriteLine("OK - {0} - {1}", IcdEnvironment.GetLocalTime(), message);
#endif
}
finally
{
s_LoggingSection.Leave();
}
} }
/// <summary>
/// Logs the message to the standard error output.
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
[PublicAPI] [PublicAPI]
public static void Ok(string message, params object[] args) public static void Ok(string message, params object[] args)
{ {
Ok(string.Format(message, args)); Log(OK, message, null, args);
} }
/// <summary>
/// Logs the exception to the standard error output.
/// </summary>
/// <param name="exception"></param>
/// <param name="message"></param>
/// <param name="args"></param>
[PublicAPI] [PublicAPI]
public static void Exception(Exception ex, string message) public static void Exception(Exception exception, string message, params object[] args)
{ {
s_LoggingSection.Enter(); Log(EXCEPTION, message, exception, args);
try
{
#if !SIMPLSHARP
message = string.Format("{0}: {1}", ex.GetType().Name, message);
#endif
message = eConsoleColor.YellowOnRed.FormatAnsi(message);
#if SIMPLSHARP
ErrorLog.Exception(message, ex);
#else
Console.Error.WriteLine("Except - {0} - {1}", IcdEnvironment.GetLocalTime(), message);
Console.Error.WriteLine(ex.StackTrace);
#endif
}
finally
{
s_LoggingSection.Leave();
}
}
[PublicAPI]
public static void Exception(Exception ex, string message, params object[] args)
{
message = string.Format(message, args);
Exception(ex, message);
}
[PublicAPI]
public static void Info(string message)
{
s_LoggingSection.Enter();
try
{
message = eConsoleColor.Cyan.FormatAnsi(message);
#if SIMPLSHARP
ErrorLog.Info(message);
#else
Console.Error.WriteLine("Info - {0} - {1}", IcdEnvironment.GetLocalTime(), message);
#endif
}
finally
{
s_LoggingSection.Leave();
}
} }
/// <summary>
/// Logs the info to the standard error output.
/// </summary>
/// <param name="message"></param>
/// <param name="args"></param>
[PublicAPI] [PublicAPI]
public static void Info(string message, params object[] args) public static void Info(string message, params object[] args)
{ {
Info(string.Format(message, args)); Log(INFO, message, null, args);
} }
#endregion
#region Private Methods
/// <summary>
/// Logs the message to the standard error output.
/// </summary>
/// <param name="severity"></param>
/// <param name="message"></param>
/// <param name="exception"></param>
/// <param name="args"></param>
private static void Log(string severity, string message, Exception exception, params object[] args)
{
message = Format(severity, message, exception, args);
s_LoggingSection.Enter();
try
{
Action<string, Exception> method = s_LogMethods[severity];
method(message, exception);
}
finally
{
s_LoggingSection.Leave();
}
}
/// <summary>
/// Formats the message into something human readable.
/// </summary>
/// <param name="severity"></param>
/// <param name="message"></param>
/// <param name="exception"></param>
/// <param name="args"></param>
/// <returns></returns>
private static string Format(string severity, string message, Exception exception, params object[] args)
{
// Insert the parameters
if (args.Length > 0)
message = string.Format(message, args);
switch (IcdEnvironment.RuntimeEnvironment)
{
case IcdEnvironment.eRuntimeEnvironment.Standard:
// Prepend the exception type, append the stack trace
if (exception != null)
message = string.Format("{0}: {1}{2}{3}",
exception.GetType().Name,
message,
IcdEnvironment.NewLine,
exception.StackTrace);
// Prefix severity and time
string fixedSeverity = severity.Substring(0, Math.Min(6, severity.Length));
fixedSeverity = string.Format("{0,-6}", fixedSeverity);
message = string.Format("{0} - {1} - {2}", fixedSeverity, IcdEnvironment.GetLocalTime(), message);
break;
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
// Add an extra newline
message += IcdEnvironment.NewLine;
break;
}
// Color formatting
return s_SeverityToColor[severity].FormatAnsi(message);
}
#endregion
} }
} }

View File

@@ -21,7 +21,7 @@ namespace ICD.Common.Utils
public static string RootPath { public static string RootPath {
get get
{ {
if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono) if (IcdEnvironment.RuntimeEnvironment == IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer)
return IcdDirectory.GetApplicationRootDirectory(); return IcdDirectory.GetApplicationRootDirectory();
return IcdDirectory.GetDirectoryRoot(IcdPath.DirectorySeparatorChar.ToString()); return IcdDirectory.GetDirectoryRoot(IcdPath.DirectorySeparatorChar.ToString());
@@ -44,7 +44,13 @@ namespace ICD.Common.Utils
get get
{ {
#if SIMPLSHARP #if SIMPLSHARP
return Join(RootPath, "User"); switch (IcdEnvironment.RuntimeEnvironment)
{
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
return Join(RootPath, "user");
default:
return Join(RootPath, "User");
}
#elif LINUX #elif LINUX
return Join(RootPath, "opt", "ICD.Connect"); return Join(RootPath, "opt", "ICD.Connect");
#else #else
@@ -72,10 +78,11 @@ namespace ICD.Common.Utils
{ {
case IcdEnvironment.eRuntimeEnvironment.SimplSharp: case IcdEnvironment.eRuntimeEnvironment.SimplSharp:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro: case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
case IcdEnvironment.eRuntimeEnvironment.Standard: case IcdEnvironment.eRuntimeEnvironment.Standard:
return string.Format("Program{0:D2}Config", ProgramUtils.ProgramNumber); return string.Format("Program{0:D2}Config", ProgramUtils.ProgramNumber);
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono: case IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer:
return "ProgramConfig"; return "ProgramConfig";
default: default:
@@ -103,10 +110,11 @@ namespace ICD.Common.Utils
{ {
case IcdEnvironment.eRuntimeEnvironment.SimplSharp: case IcdEnvironment.eRuntimeEnvironment.SimplSharp:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro: case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
case IcdEnvironment.eRuntimeEnvironment.Standard: case IcdEnvironment.eRuntimeEnvironment.Standard:
return string.Format("Program{0:D2}Data", ProgramUtils.ProgramNumber); return string.Format("Program{0:D2}Data", ProgramUtils.ProgramNumber);
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono: case IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer:
return "ProgramData"; return "ProgramData";
default: default:
@@ -151,11 +159,12 @@ namespace ICD.Common.Utils
{ {
case IcdEnvironment.eRuntimeEnvironment.SimplSharp: case IcdEnvironment.eRuntimeEnvironment.SimplSharp:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro: case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
case IcdEnvironment.eRuntimeEnvironment.Standard: case IcdEnvironment.eRuntimeEnvironment.Standard:
directoryName = string.Format("Program{0:D2}Logs", ProgramUtils.ProgramNumber); directoryName = string.Format("Program{0:D2}Logs", ProgramUtils.ProgramNumber);
break; break;
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono: case IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer:
directoryName = "ProgramLogs"; directoryName = "ProgramLogs";
break; break;
@@ -183,6 +192,9 @@ namespace ICD.Common.Utils
return Join(RootPath, "HTML"); return Join(RootPath, "HTML");
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono: case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
return Join(RootPath, "html");
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer:
return Join(RootPath, "Html"); return Join(RootPath, "Html");
case IcdEnvironment.eRuntimeEnvironment.Standard: case IcdEnvironment.eRuntimeEnvironment.Standard:

View File

@@ -11,8 +11,10 @@ namespace ICD.Common.Utils
{ {
public static partial class ProcessorUtils public static partial class ProcessorUtils
{ {
// CP3 - CP3 Cntrl Eng [v1.601.3934.19631 (Oct 10 2019), #00A3BBE7] @E-00107f4a3474
// CP4 - CP4 Cntrl Eng [v2.4474.00005 (Apr 9 2020), #8EB216B7] @E-00107feb538f
private const string VER_REGEX = private const string VER_REGEX =
@"(?'model'\S+) (?'type'\S+) (?'lang'\S+) \[v(?'version'\d+.\d+.\d+.\d+) \((?'date'\S+ \d+ \d+)\), #(?'serial'[A-F0-9]+)\] @E-(?'mac'[a-z0-9]+)"; @"(?'model'\S+)\s+(?'type'\S+)\s+(?'lang'\S+)\s+\[v(?'version'\d+(\.\d+)+)\s+\((?'date'\S+\s+\d+ \d+)\),\s+#(?'serial'[A-F0-9]+)\]\s+@E-(?'mac'[a-z0-9]+)";
private const string UPTIME_COMMAND = "uptime"; private const string UPTIME_COMMAND = "uptime";
private const string PROGUPTIME_COMMAND_ROOT = "proguptime:{0}"; private const string PROGUPTIME_COMMAND_ROOT = "proguptime:{0}";
@@ -108,13 +110,33 @@ namespace ICD.Common.Utils
Regex regex = new Regex(VER_REGEX); Regex regex = new Regex(VER_REGEX);
Match match = regex.Match(VersionResult); Match match = regex.Match(VersionResult);
if (match.Success) if (!match.Success)
return DateTime.ParseExact(match.Groups["date"].Value, "MMM dd yyyy", CultureInfo.InvariantCulture).ToUniversalTime(); {
ServiceProvider.TryGetService<ILoggerService>()
.AddEntry(eSeverity.Warning, "Unable to get model version date from \"{0}\"", VersionResult);
return DateTime.MinValue;
}
ServiceProvider.TryGetService<ILoggerService>() string date = match.Groups["date"].Value;
.AddEntry(eSeverity.Warning, "Unable to get model version date from \"{0}\"", VersionResult);
try
return DateTime.MinValue; {
switch (IcdEnvironment.RuntimeEnvironment)
{
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
date = StringUtils.RemoveDuplicateWhitespace(date);
return DateTime.ParseExact(date, "MMM d yyyy", CultureInfo.InvariantCulture).ToUniversalTime();
default:
return DateTime.ParseExact(date, "MMM dd yyyy", CultureInfo.InvariantCulture).ToUniversalTime();
}
}
catch (FormatException)
{
ServiceProvider.TryGetService<ILoggerService>()
.AddEntry(eSeverity.Warning, "Failed to parse date \"{0}\"", date);
return DateTime.MinValue;
}
} }
} }

View File

@@ -29,11 +29,12 @@ namespace ICD.Common.Utils
break; break;
case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro: case IcdEnvironment.eRuntimeEnvironment.SimplSharpPro:
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono:
int proLength = Math.Min(26 - 1, name.Length); int proLength = Math.Min(26 - 1, name.Length);
name = name.Substring(0, proLength).PadRight(26); name = name.Substring(0, proLength).PadRight(26);
break; break;
case IcdEnvironment.eRuntimeEnvironment.SimplSharpProMono: case IcdEnvironment.eRuntimeEnvironment.SimplSharpProServer:
// No console // No console
return; return;

View File

@@ -4,4 +4,4 @@ using System.Reflection;
[assembly: AssemblyCompany("ICD Systems")] [assembly: AssemblyCompany("ICD Systems")]
[assembly: AssemblyProduct("ICD.Common.Utils")] [assembly: AssemblyProduct("ICD.Common.Utils")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2020")] [assembly: AssemblyCopyright("Copyright © ICD Systems 2020")]
[assembly: AssemblyVersion("12.1.0.0")] [assembly: AssemblyVersion("13.0.0.0")]

View File

@@ -647,6 +647,16 @@ namespace ICD.Common.Utils
return text == null ? null : text.RemoveWhitespace(); return text == null ? null : text.RemoveWhitespace();
} }
/// <summary>
/// Replaces spans of whitespace with a single space.
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string RemoveDuplicateWhitespace(string text)
{
return text == null ? null : text.RemoveDuplicateWhitespace();
}
/// <summary> /// <summary>
/// Returns true if the string is entirely whitespace characters, or empty, or null. /// Returns true if the string is entirely whitespace characters, or empty, or null.
/// </summary> /// </summary>