Private/SampleJson.ps1

#Requires -Version 5.1

function Initialize-SMBeatSampleJsonNative {
    if ($script:SMBeatSampleJsonReady) {
        return $true
    }
    if ($script:SMBeatSampleJsonFailed) {
        return $false
    }
    if ('SMBeat.Native.SampleAggregator' -as [type]) {
        $script:SMBeatSampleJsonReady = $true
        return $true
    }
    if ('SMBeat.Native.SampleJson' -as [type]) {
        $script:SMBeatSampleJsonFailed = $true
        return $false
    }

    $code = @'
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
 
namespace SMBeat.Native
{
    public sealed class SampleRecord
    {
        public string ts { get; set; }
        public DateTime TsUtc { get; set; }
        public string server { get; set; }
        public string kind { get; set; }
        public string name { get; set; }
        public string client { get; set; }
        public string user { get; set; }
        public long readBytesDelta { get; set; }
        public long writeBytesDelta { get; set; }
        public long sentBytesDelta { get; set; }
        public long receivedBytesDelta { get; set; }
        public int sampleIntervalSec { get; set; }
    }
 
    public sealed class SampleWindowPair
    {
        public List<SampleRecord> Previous = new List<SampleRecord>();
        public List<SampleRecord> Current = new List<SampleRecord>();
    }
 
    public static class SampleJson
    {
        public static SampleRecord ParseLine(string line)
        {
            if (string.IsNullOrWhiteSpace(line))
            {
                return null;
            }
 
            int i = 0;
            SkipWs(line, ref i);
            if (i >= line.Length || line[i] != '{')
            {
                return null;
            }
            i++;
 
            SampleRecord rec = new SampleRecord();
            StringBuilder sb = new StringBuilder(64);
            bool any = false;
            while (i < line.Length)
            {
                SkipWs(line, ref i);
                if (i >= line.Length)
                {
                    return null;
                }
                if (line[i] == '}')
                {
                    break;
                }
                if (line[i] == ',')
                {
                    i++;
                    continue;
                }
                if (line[i] != '"')
                {
                    return null;
                }
 
                string key;
                if (!TryReadString(line, ref i, sb, out key))
                {
                    return null;
                }
                SkipWs(line, ref i);
                if (i >= line.Length || line[i] != ':')
                {
                    return null;
                }
                i++;
                SkipWs(line, ref i);
                if (i >= line.Length)
                {
                    return null;
                }
 
                if (line[i] == '"')
                {
                    string text;
                    if (!TryReadString(line, ref i, sb, out text))
                    {
                        return null;
                    }
                    AssignString(rec, key, text);
                    any = true;
                }
                else if (line[i] == 'n' || line[i] == 'N')
                {
                    if (!TrySkipLiteral(line, ref i, "null"))
                    {
                        return null;
                    }
                    AssignString(rec, key, null);
                    any = true;
                }
                else if (line[i] == 't' || line[i] == 'T' || line[i] == 'f' || line[i] == 'F')
                {
                    if (!TrySkipBool(line, ref i))
                    {
                        return null;
                    }
                }
                else
                {
                    long n;
                    if (!TryReadInt64(line, ref i, out n))
                    {
                        return null;
                    }
                    AssignNumber(rec, key, n);
                    any = true;
                }
            }
 
            if (!any || string.IsNullOrEmpty(rec.ts))
            {
                return null;
            }
 
            DateTime ts;
            if (!TryParseTs(rec.ts, out ts))
            {
                return null;
            }
            rec.TsUtc = ts;
            return rec;
        }
 
        public static List<SampleRecord> ReadFile(string path, DateTime startUtc, DateTime endUtc)
        {
            List<SampleRecord> list = new List<SampleRecord>();
            if (string.IsNullOrEmpty(path) || !File.Exists(path))
            {
                return list;
            }
 
            foreach (string line in File.ReadLines(path, Encoding.UTF8))
            {
                SampleRecord rec = ParseLine(line);
                if (rec == null)
                {
                    continue;
                }
                if (rec.TsUtc >= startUtc && rec.TsUtc < endUtc)
                {
                    list.Add(rec);
                }
            }
            return list;
        }
 
        public static SampleWindowPair ReadDirectory(
            string dir,
            DateTime prevStart,
            DateTime prevEnd,
            DateTime windowStart,
            DateTime windowEnd)
        {
            SampleWindowPair pair = new SampleWindowPair();
            if (string.IsNullOrEmpty(dir) || !Directory.Exists(dir))
            {
                return pair;
            }
 
            DateTime spanStart = prevStart < windowStart ? prevStart : windowStart;
            DateTime spanEnd = prevEnd > windowEnd ? prevEnd : windowEnd;
            DateTime startDay = spanStart.Date.AddDays(-1);
            DateTime endDay = spanEnd.Date.AddDays(1);
 
            string[] files = Directory.GetFiles(dir, "*.jsonl");
            Array.Sort(files, StringComparer.OrdinalIgnoreCase);
            foreach (string path in files)
            {
                string dayPart = Path.GetFileNameWithoutExtension(path);
                DateTime fileDay;
                if (!DateTime.TryParseExact(dayPart, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out fileDay))
                {
                    continue;
                }
                if (fileDay < startDay || fileDay > endDay)
                {
                    continue;
                }
 
                foreach (string line in File.ReadLines(path, Encoding.UTF8))
                {
                    SampleRecord rec = ParseLine(line);
                    if (rec == null)
                    {
                        continue;
                    }
                    if (rec.TsUtc >= prevStart && rec.TsUtc < prevEnd)
                    {
                        pair.Previous.Add(rec);
                    }
                    if (rec.TsUtc >= windowStart && rec.TsUtc < windowEnd)
                    {
                        pair.Current.Add(rec);
                    }
                }
            }
            return pair;
        }
 
        private static void AssignString(SampleRecord rec, string key, string value)
        {
            if (key == "ts") { rec.ts = value; }
            else if (key == "server") { rec.server = value; }
            else if (key == "kind") { rec.kind = value; }
            else if (key == "name") { rec.name = value; }
            else if (key == "client") { rec.client = value; }
            else if (key == "user") { rec.user = value; }
        }
 
        private static void AssignNumber(SampleRecord rec, string key, long value)
        {
            if (key == "readBytesDelta") { rec.readBytesDelta = value; }
            else if (key == "writeBytesDelta") { rec.writeBytesDelta = value; }
            else if (key == "sentBytesDelta") { rec.sentBytesDelta = value; }
            else if (key == "receivedBytesDelta") { rec.receivedBytesDelta = value; }
            else if (key == "sampleIntervalSec") { rec.sampleIntervalSec = (int)value; }
        }
 
        private static bool TryParseTs(string value, out DateTime ts)
        {
            if (DateTime.TryParseExact(value, "yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out ts))
            {
                return true;
            }
            return DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out ts);
        }
 
        private static void SkipWs(string s, ref int i)
        {
            while (i < s.Length && char.IsWhiteSpace(s[i]))
            {
                i++;
            }
        }
 
        private static bool TrySkipLiteral(string s, ref int i, string lit)
        {
            if (i + lit.Length > s.Length)
            {
                return false;
            }
            for (int k = 0; k < lit.Length; k++)
            {
                if (char.ToLowerInvariant(s[i + k]) != lit[k])
                {
                    return false;
                }
            }
            i += lit.Length;
            return true;
        }
 
        private static bool TrySkipBool(string s, ref int i)
        {
            if (s[i] == 't' || s[i] == 'T')
            {
                return TrySkipLiteral(s, ref i, "true");
            }
            return TrySkipLiteral(s, ref i, "false");
        }
 
        private static bool TryReadInt64(string s, ref int i, out long value)
        {
            value = 0;
            int start = i;
            if (i < s.Length && (s[i] == '-' || s[i] == '+'))
            {
                i++;
            }
            bool digit = false;
            while (i < s.Length && s[i] >= '0' && s[i] <= '9')
            {
                digit = true;
                i++;
            }
            if (i < s.Length && s[i] == '.')
            {
                i++;
                while (i < s.Length && s[i] >= '0' && s[i] <= '9')
                {
                    i++;
                }
            }
            if (!digit)
            {
                return false;
            }
            string raw = s.Substring(start, i - start);
            double d;
            if (!double.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out d))
            {
                return false;
            }
            if (d >= long.MaxValue)
            {
                value = long.MaxValue;
            }
            else if (d <= long.MinValue)
            {
                value = long.MinValue;
            }
            else
            {
                value = (long)d;
            }
            return true;
        }
 
        private static bool TryReadString(string s, ref int i, StringBuilder sb, out string value)
        {
            value = null;
            if (i >= s.Length || s[i] != '"')
            {
                return false;
            }
            i++;
            sb.Length = 0;
            while (i < s.Length)
            {
                char c = s[i];
                if (c == '"')
                {
                    i++;
                    value = sb.ToString();
                    return true;
                }
                if (c == '\\')
                {
                    i++;
                    if (i >= s.Length)
                    {
                        return false;
                    }
                    char e = s[i];
                    if (e == '"' || e == '\\' || e == '/')
                    {
                        sb.Append(e);
                    }
                    else if (e == 'b') { sb.Append('\b'); }
                    else if (e == 'f') { sb.Append('\f'); }
                    else if (e == 'n') { sb.Append('\n'); }
                    else if (e == 'r') { sb.Append('\r'); }
                    else if (e == 't') { sb.Append('\t'); }
                    else if (e == 'u' && i + 4 < s.Length)
                    {
                        int hex;
                        if (!int.TryParse(s.Substring(i + 1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out hex))
                        {
                            return false;
                        }
                        sb.Append((char)hex);
                        i += 4;
                    }
                    else
                    {
                        sb.Append(e);
                    }
                    i++;
                    continue;
                }
                sb.Append(c);
                i++;
            }
            return false;
        }
    }
 
    public sealed class NamedAcc
    {
        public string Name;
        public long Read;
        public long Write;
        public long Sent;
        public long Received;
    }
 
    public sealed class SeriesRow
    {
        public string Granularity;
        public DateTime PeriodStart;
        public DateTime PeriodEnd;
        public long ReadBytes;
        public long WriteBytes;
        public long SentBytes;
        public long ReceivedBytes;
        public long NicBytesIn;
        public long NicBytesOut;
        public long RetrievedBytes;
        public long WrittenBytes;
    }
 
    public sealed class AggregateResult
    {
        public long ReadBytes;
        public long WriteBytes;
        public long SentBytes;
        public long ReceivedBytes;
        public long NicBytesIn;
        public long NicBytesOut;
        public long RetrievedBytes;
        public long WrittenBytes;
        public int ListenCount;
        public int ServerKindCount;
        public bool WarnNicOut;
        public bool WarnEstataRead;
        public bool WarnShareLow;
        public bool WarnEtwWritten;
        public List<NamedAcc> ByServer = new List<NamedAcc>();
        public List<NamedAcc> ByShare = new List<NamedAcc>();
        public List<NamedAcc> ByClient = new List<NamedAcc>();
        public List<NamedAcc> ByUser = new List<NamedAcc>();
        public List<SeriesRow> Hourly = new List<SeriesRow>();
        public List<SeriesRow> Daily = new List<SeriesRow>();
        public List<SeriesRow> Weekly = new List<SeriesRow>();
        public List<SeriesRow> HourlyForPeak = new List<SeriesRow>();
    }
 
    public static class SampleAggregator
    {
        private sealed class Acc
        {
            public long Read;
            public long Write;
            public long Sent;
            public long Received;
 
            public void Add(long r, long w, long s, long v)
            {
                Read += r;
                Write += w;
                Sent += s;
                Received += v;
            }
        }
 
        private sealed class SeriesBucket
        {
            public DateTime Start;
            public long Read;
            public long Write;
            public long Sent;
            public long Received;
            public long ServerRead;
            public long ServerWrite;
            public long ServerSent;
            public long ServerRecv;
            public bool HasShare;
            public long NicIn;
            public long NicOut;
            public long Tcp445Sent;
            public long Tcp445Recv;
            public bool HasTcp445;
        }
 
        public static AggregateResult Run(
            SampleRecord[] records,
            DateTime startUtc,
            DateTime endUtc,
            string timeZoneId,
            string[] granularity,
            int intervalSec,
            bool totalsOnly)
        {
            AggregateResult result = new AggregateResult();
            if (records == null)
            {
                records = new SampleRecord[0];
            }
            if (granularity == null)
            {
                granularity = new string[0];
            }
 
            TimeZoneInfo tz = ResolveTimeZone(timeZoneId);
 
            Dictionary<string, Acc> byServerFromServer = new Dictionary<string, Acc>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, Acc> byServerFromShares = new Dictionary<string, Acc>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, Acc> byServerFromUsers = new Dictionary<string, Acc>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, Acc> byServerFromTcp = new Dictionary<string, Acc>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, Acc> byShare = new Dictionary<string, Acc>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, Acc> byClient = new Dictionary<string, Acc>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, Acc> byClientTcp = new Dictionary<string, Acc>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, Acc> byClientEtw = new Dictionary<string, Acc>(StringComparer.OrdinalIgnoreCase);
            Dictionary<string, Acc> byUser = new Dictionary<string, Acc>(StringComparer.OrdinalIgnoreCase);
 
            long serverRead = 0;
            long serverWrite = 0;
            long serverSent = 0;
            long serverRecv = 0;
            long shareRead = 0;
            long shareWrite = 0;
            long shareSent = 0;
            long shareRecv = 0;
            long nicIn = 0;
            long nicOut = 0;
            long tcp445Sent = 0;
            long tcp445Recv = 0;
            bool hasTcp445 = false;
            int listenCount = 0;
            int serverKindCount = 0;
 
            bool wantHour = !totalsOnly;
            bool wantDay = !totalsOnly && Wants(granularity, "Day");
            bool wantWeek = !totalsOnly && Wants(granularity, "Week");
            SortedDictionary<DateTime, SeriesBucket> hourMap = new SortedDictionary<DateTime, SeriesBucket>();
            SortedDictionary<DateTime, SeriesBucket> dayMap = new SortedDictionary<DateTime, SeriesBucket>();
            SortedDictionary<DateTime, SeriesBucket> weekMap = new SortedDictionary<DateTime, SeriesBucket>();
 
            for (int i = 0; i < records.Length; i++)
            {
                SampleRecord rec = records[i];
                if (rec == null)
                {
                    continue;
                }
 
                long r = rec.readBytesDelta;
                long w = rec.writeBytesDelta;
                long s = rec.sentBytesDelta;
                long v = rec.receivedBytesDelta;
                string kind = rec.kind != null ? rec.kind : "";
                string serverName = rec.server != null ? rec.server : "";
 
                if (kind == "server")
                {
                    serverKindCount++;
                    serverRead += r;
                    serverWrite += w;
                    serverSent += s;
                    serverRecv += v;
                    AddAcc(byServerFromServer, serverName, r, w, s, v);
                }
                else if (kind == "share")
                {
                    shareRead += r;
                    shareWrite += w;
                    shareSent += s;
                    shareRecv += v;
                    string shareName = rec.name != null ? rec.name : "";
                    AddAcc(byShare, shareName, r, w, s, v);
                    AddAcc(byServerFromShares, serverName, r, w, s, v);
                }
                else if (kind == "session")
                {
                    string clientKey = rec.client;
                    if (string.IsNullOrWhiteSpace(clientKey))
                    {
                        clientKey = rec.name;
                    }
                    string userKey = rec.user;
                    if (string.IsNullOrWhiteSpace(userKey))
                    {
                        userKey = "(unknown)";
                    }
                    if (!string.IsNullOrWhiteSpace(clientKey))
                    {
                        AddAcc(byClient, clientKey, r, w, s, v);
                    }
                    AddAcc(byUser, userKey, r, w, s, v);
                }
                else if (kind == "client")
                {
                    string clientKey = rec.client;
                    if (string.IsNullOrWhiteSpace(clientKey))
                    {
                        clientKey = rec.name;
                    }
                    if (!string.IsNullOrWhiteSpace(clientKey))
                    {
                        AddAcc(byClientEtw, clientKey, r, w, s, v);
                    }
                }
                else if (kind == "user")
                {
                    string userKey = rec.user;
                    if (string.IsNullOrWhiteSpace(userKey))
                    {
                        userKey = rec.name;
                    }
                    if (string.IsNullOrWhiteSpace(userKey))
                    {
                        userKey = "(unknown)";
                    }
                    AddAcc(byUser, userKey, r, w, s, v);
                    if (!string.IsNullOrWhiteSpace(serverName))
                    {
                        AddAcc(byServerFromUsers, serverName, r, w, s, v);
                    }
                }
                else if (kind == "tcp445")
                {
                    hasTcp445 = true;
                    string name = rec.name != null ? rec.name : "";
                    if (name == "_listen445")
                    {
                        listenCount++;
                        tcp445Sent += s;
                        tcp445Recv += v;
                        AddAcc(byServerFromTcp, serverName, 0, 0, s, v);
                    }
                    else
                    {
                        string clientKey = rec.client;
                        if (string.IsNullOrWhiteSpace(clientKey))
                        {
                            clientKey = name;
                        }
                        AddAcc(byClientTcp, clientKey, 0, 0, s, v);
                    }
                }
                else if (kind == "nic")
                {
                    nicIn += v;
                    nicOut += s;
                }
 
                if (wantHour || wantDay || wantWeek)
                {
                    DateTime local = ToLocal(rec.TsUtc, tz);
                    if (wantHour)
                    {
                        AddSeriesDelta(GetBucket(hourMap, BucketStart(local, "Hour")), rec);
                    }
                    if (wantDay)
                    {
                        AddSeriesDelta(GetBucket(dayMap, BucketStart(local, "Day")), rec);
                    }
                    if (wantWeek)
                    {
                        AddSeriesDelta(GetBucket(weekMap, BucketStart(local, "Week")), rec);
                    }
                }
            }
 
            bool useShareTotals = (shareSent + shareRecv) > (serverSent + serverRecv);
            long read = serverRead;
            long write = serverWrite;
            long sent = serverSent;
            long recv = serverRecv;
            Dictionary<string, Acc> byServer = byServerFromServer;
            if (useShareTotals)
            {
                read = shareRead;
                write = shareWrite;
                sent = shareSent;
                recv = shareRecv;
                byServer = byServerFromShares;
            }
            if (hasTcp445)
            {
                byServer = byServerFromTcp;
                if (byClientTcp.Count > 0)
                {
                    byClient = byClientTcp;
                }
            }
            if (byClientEtw.Count > 0)
            {
                if (byClient.Count == 0)
                {
                    byClient = byClientEtw;
                }
                else
                {
                    foreach (KeyValuePair<string, Acc> ekv in byClientEtw)
                    {
                        Acc etw = ekv.Value;
                        Acc cur;
                        if (!byClient.TryGetValue(ekv.Key, out cur))
                        {
                            byClient[ekv.Key] = etw;
                            continue;
                        }
                        if (etw.Sent > 1048576 && cur.Sent > (long)((double)etw.Sent * 1.1))
                        {
                            cur.Sent = etw.Sent;
                        }
                        else if (etw.Sent > cur.Sent)
                        {
                            cur.Sent = etw.Sent;
                        }
                        if (etw.Received > cur.Received) { cur.Received = etw.Received; }
                        if (etw.Read > cur.Read) { cur.Read = etw.Read; }
                        if (etw.Write > cur.Write) { cur.Write = etw.Write; }
                    }
                }
            }
 
            long retrieved = nicOut;
            long written = recv;
            if (hasTcp445)
            {
                retrieved = tcp445Sent;
                written = tcp445Recv;
            }
            else if (nicOut > 104857600 && nicOut > (10 * Math.Max(sent, 1L)))
            {
                result.WarnNicOut = true;
            }
 
            long etwRetrieved = EtwRetrieved(byUser, shareSent);
            long etwWritten = shareRecv;
            foreach (KeyValuePair<string, Acc> ukv in byUser)
            {
                if (ukv.Value.Received > etwWritten)
                {
                    etwWritten = ukv.Value.Received;
                }
            }
 
            bool retrievedFromEtw = false;
            if (hasTcp445 && nicOut > 1048576 && retrieved > nicOut && retrieved > (long)((double)nicOut * 1.1))
            {
                if (etwRetrieved > 1048576 && VolumeNear(etwRetrieved, nicOut))
                {
                    retrieved = etwRetrieved;
                    retrievedFromEtw = true;
                    result.WarnEstataRead = true;
                }
            }
 
            if (hasTcp445 && retrieved > 10485760)
            {
                long shareFloor = (long)Math.Floor(retrieved * 0.1);
                if (shareSent < shareFloor)
                {
                    result.WarnShareLow = true;
                }
            }
 
            if (hasTcp445 && etwWritten > 1048576 && etwWritten > (10 * Math.Max(written, 1L)))
            {
                written = etwWritten;
                result.WarnEtwWritten = true;
            }
 
            List<string> serverKeys = new List<string>(byServer.Keys);
            for (int si = 0; si < serverKeys.Count; si++)
            {
                string sk = serverKeys[si];
                long etwRecv = 0;
                long etwSent = 0;
                Acc shareAcc;
                if (byServerFromShares.TryGetValue(sk, out shareAcc))
                {
                    etwRecv = shareAcc.Received;
                    etwSent = shareAcc.Sent;
                }
                Acc userAcc;
                if (byServerFromUsers.TryGetValue(sk, out userAcc))
                {
                    if (userAcc.Received > etwRecv)
                    {
                        etwRecv = userAcc.Received;
                    }
                    if (userAcc.Sent > etwSent)
                    {
                        etwSent = userAcc.Sent;
                    }
                }
                Acc cur = byServer[sk];
                if (etwRecv > 1048576 && etwRecv > (10 * Math.Max(cur.Received, 1L)))
                {
                    cur.Received = etwRecv;
                }
                if (retrievedFromEtw && etwSent > 1048576)
                {
                    cur.Sent = etwSent;
                }
                else if (etwSent > 1048576 && cur.Sent > (long)((double)etwSent * 1.1) && nicOut > 1048576 && VolumeNear(etwSent, nicOut))
                {
                    cur.Sent = etwSent;
                }
            }
 
            if (hasTcp445 && byServer.Count == 1)
            {
                foreach (KeyValuePair<string, Acc> skv in byServer)
                {
                    skv.Value.Sent = retrieved;
                    skv.Value.Received = written;
                }
            }
 
            result.ReadBytes = read;
            result.WriteBytes = write;
            result.SentBytes = sent;
            result.ReceivedBytes = recv;
            result.NicBytesIn = nicIn;
            result.NicBytesOut = nicOut;
            result.RetrievedBytes = retrieved;
            result.WrittenBytes = written;
            result.ListenCount = listenCount;
            result.ServerKindCount = serverKindCount;
            result.ByServer = ToNamedList(byServer);
            result.ByShare = ToNamedList(byShare);
            result.ByClient = ToNamedList(byClient);
            result.ByUser = ToNamedList(byUser);
 
            if (!totalsOnly)
            {
                List<SeriesRow> hourRows = ToSeriesRows(hourMap, "Hour");
                result.HourlyForPeak = hourRows;
                if (Wants(granularity, "Hour"))
                {
                    result.Hourly = hourRows;
                }
                if (wantDay)
                {
                    result.Daily = ToSeriesRows(dayMap, "Day");
                }
                if (wantWeek)
                {
                    result.Weekly = ToSeriesRows(weekMap, "Week");
                }
            }
 
            return result;
        }
 
        private static TimeZoneInfo ResolveTimeZone(string timeZoneId)
        {
            if (string.IsNullOrEmpty(timeZoneId))
            {
                return TimeZoneInfo.Utc;
            }
            try
            {
                return TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
            }
            catch (TimeZoneNotFoundException)
            {
                return TimeZoneInfo.Utc;
            }
            catch (InvalidTimeZoneException)
            {
                return TimeZoneInfo.Utc;
            }
        }
 
        private static DateTime ToLocal(DateTime utc, TimeZoneInfo tz)
        {
            DateTime asUtc = utc;
            if (asUtc.Kind == DateTimeKind.Local)
            {
                asUtc = asUtc.ToUniversalTime();
            }
            else
            {
                asUtc = DateTime.SpecifyKind(asUtc, DateTimeKind.Utc);
            }
            return TimeZoneInfo.ConvertTimeFromUtc(asUtc, tz);
        }
 
        private static DateTime BucketStart(DateTime local, string gran)
        {
            DateTimeKind kind = local.Kind;
            if (gran == "Hour")
            {
                return new DateTime(local.Year, local.Month, local.Day, local.Hour, 0, 0, kind);
            }
            if (gran == "Day")
            {
                return new DateTime(local.Year, local.Month, local.Day, 0, 0, 0, kind);
            }
            DateTime date = new DateTime(local.Year, local.Month, local.Day, 0, 0, 0, kind);
            int offset = ((int)date.DayOfWeek + 6) % 7;
            return date.AddDays(-offset);
        }
 
        private static DateTime BucketEnd(DateTime start, string gran)
        {
            if (gran == "Hour")
            {
                return start.AddHours(1);
            }
            if (gran == "Day")
            {
                return start.AddDays(1);
            }
            return start.AddDays(7);
        }
 
        private static bool Wants(string[] granularity, string name)
        {
            if (granularity == null)
            {
                return false;
            }
            for (int i = 0; i < granularity.Length; i++)
            {
                if (granularity[i] == name)
                {
                    return true;
                }
            }
            return false;
        }
 
        private static void AddAcc(Dictionary<string, Acc> map, string key, long r, long w, long s, long v)
        {
            if (key == null)
            {
                key = "";
            }
            Acc acc;
            if (!map.TryGetValue(key, out acc))
            {
                acc = new Acc();
                map[key] = acc;
            }
            acc.Add(r, w, s, v);
        }
 
        private static bool VolumeNear(long left, long right)
        {
            long hi = left > right ? left : right;
            long lo = left < right ? left : right;
            if (hi <= 0)
            {
                return true;
            }
            return ((double)lo / (double)hi) >= 0.8;
        }
 
        private static long EtwRetrieved(Dictionary<string, Acc> byUser, long shareSent)
        {
            long userSent = 0;
            foreach (KeyValuePair<string, Acc> kv in byUser)
            {
                userSent += kv.Value.Sent;
            }
            if (userSent > shareSent)
            {
                return userSent;
            }
            return shareSent;
        }
 
        private static SeriesBucket GetBucket(SortedDictionary<DateTime, SeriesBucket> map, DateTime start)
        {
            SeriesBucket b;
            if (!map.TryGetValue(start, out b))
            {
                b = new SeriesBucket();
                b.Start = start;
                map[start] = b;
            }
            return b;
        }
 
        private static void AddSeriesDelta(SeriesBucket bucket, SampleRecord rec)
        {
            string kind = rec.kind != null ? rec.kind : "";
            if (kind == "server")
            {
                bucket.ServerRead += rec.readBytesDelta;
                bucket.ServerWrite += rec.writeBytesDelta;
                bucket.ServerSent += rec.sentBytesDelta;
                bucket.ServerRecv += rec.receivedBytesDelta;
            }
            else if (kind == "share")
            {
                bucket.Read += rec.readBytesDelta;
                bucket.Write += rec.writeBytesDelta;
                bucket.Sent += rec.sentBytesDelta;
                bucket.Received += rec.receivedBytesDelta;
                bucket.HasShare = true;
            }
            else if (kind == "tcp445")
            {
                string name = rec.name != null ? rec.name : "";
                if (name == "_listen445")
                {
                    bucket.Tcp445Sent += rec.sentBytesDelta;
                    bucket.Tcp445Recv += rec.receivedBytesDelta;
                    bucket.HasTcp445 = true;
                }
            }
            else if (kind == "nic")
            {
                bucket.NicIn += rec.receivedBytesDelta;
                bucket.NicOut += rec.sentBytesDelta;
            }
        }
 
        private static List<SeriesRow> ToSeriesRows(SortedDictionary<DateTime, SeriesBucket> buckets, string granularity)
        {
            List<SeriesRow> result = new List<SeriesRow>(buckets.Count);
            foreach (KeyValuePair<DateTime, SeriesBucket> kv in buckets)
            {
                SeriesBucket b = kv.Value;
                bool useShare = b.HasShare;
                long readB = b.ServerRead;
                long writeB = b.ServerWrite;
                long sentB = b.ServerSent;
                long recvB = b.ServerRecv;
                if (useShare)
                {
                    readB = b.Read;
                    writeB = b.Write;
                    sentB = b.Sent;
                    recvB = b.Received;
                }
                long retrievedB = b.NicOut;
                long writtenB = recvB;
                if (b.HasTcp445)
                {
                    retrievedB = b.Tcp445Sent;
                    writtenB = b.Tcp445Recv;
                    if (b.HasShare && b.Received > writtenB && b.Received > (10 * Math.Max(writtenB, 1L)))
                    {
                        writtenB = b.Received;
                    }
                    if (b.NicOut > 1048576 && b.Tcp445Sent > b.NicOut && b.Tcp445Sent > (long)((double)b.NicOut * 1.1))
                    {
                        long etwRead = 0;
                        if (b.HasShare)
                        {
                            etwRead = b.Sent;
                        }
                        if (etwRead > 1048576 && VolumeNear(etwRead, b.NicOut))
                        {
                            retrievedB = etwRead;
                        }
                    }
                }
                SeriesRow row = new SeriesRow();
                row.Granularity = granularity;
                row.PeriodStart = b.Start;
                row.PeriodEnd = BucketEnd(b.Start, granularity);
                row.ReadBytes = readB;
                row.WriteBytes = writeB;
                row.SentBytes = sentB;
                row.ReceivedBytes = recvB;
                row.NicBytesIn = b.NicIn;
                row.NicBytesOut = b.NicOut;
                row.RetrievedBytes = retrievedB;
                row.WrittenBytes = writtenB;
                result.Add(row);
            }
            return result;
        }
 
        private static List<NamedAcc> ToNamedList(Dictionary<string, Acc> map)
        {
            List<NamedAcc> list = new List<NamedAcc>(map.Count);
            foreach (KeyValuePair<string, Acc> kv in map)
            {
                NamedAcc n = new NamedAcc();
                n.Name = kv.Key;
                n.Read = kv.Value.Read;
                n.Write = kv.Value.Write;
                n.Sent = kv.Value.Sent;
                n.Received = kv.Value.Received;
                list.Add(n);
            }
            list.Sort(CompareNamed);
            return list;
        }
 
        private static int CompareNamed(NamedAcc a, NamedAcc b)
        {
            long aMax = a.Sent > a.Received ? a.Sent : a.Received;
            long bMax = b.Sent > b.Received ? b.Sent : b.Received;
            return bMax.CompareTo(aMax);
        }
    }
}
'@


    try {
        Add-Type -TypeDefinition $code -Language CSharp -ErrorAction Stop
        $script:SMBeatSampleJsonReady = $true
        return $true
    }
    catch {
        $script:SMBeatSampleJsonFailed = $true
        return $false
    }
}