private void DoCount(WI_Count req)
        {
            var promise = req.Promise;

            try {
                Channel?ch = GetChannelOrNull(req.Variable);
                if (ch == null)
                {
                    promise.SetResult(0);
                }
                else
                {
                    Timestamp     start  = req.StartInclusive;
                    Timestamp     end    = req.EndInclusive;
                    QualityFilter filter = req.Filter;

                    long res;
                    if (start == Timestamp.Empty && end == Timestamp.Max && filter == QualityFilter.ExcludeNone)
                    {
                        res = ch.CountAll();
                    }
                    else
                    {
                        res = ch.CountData(start, end, Map(filter));
                    }

                    promise.SetResult(res);
                }
            }
            catch (Exception exp) {
                promise.SetException(exp);
            }
        }
 public WI_Count(VariableRef variable, Timestamp startInclusive, Timestamp endInclusive, QualityFilter filter, TaskCompletionSource <long> promise)
 {
     Variable       = variable;
     StartInclusive = startInclusive;
     EndInclusive   = endInclusive;
     Filter         = filter;
     Promise        = promise;
 }
 public WI_ReadRaw(VariableRef variable, Timestamp startInclusive, Timestamp endInclusive, int maxValues, BoundingMethod bounding, QualityFilter filter, TaskCompletionSource <List <VTTQ> > promise)
 {
     Variable       = variable;
     StartInclusive = startInclusive;
     EndInclusive   = endInclusive;
     MaxValues      = maxValues;
     Bounding       = bounding;
     Filter         = filter;
     Promise        = promise;
 }
        private Timeseries.QualityFilter Map(QualityFilter f)
        {
            switch (f)
            {
            case QualityFilter.ExcludeNone: return(Timeseries.QualityFilter.ExcludeNone);

            case QualityFilter.ExcludeBad: return(Timeseries.QualityFilter.ExcludeBad);

            case QualityFilter.ExcludeNonGood: return(Timeseries.QualityFilter.ExcludeNonGood);
            }
            throw new Exception("Unknown quality filter");
        }
Beispiel #5
0
        public async Task <ReqResult> UiReq_LoadData(TimeRange timeRange)
        {
            LastTimeRange = timeRange;

            Timestamp tStart        = timeRange.GetStart();
            Timestamp tEnd          = timeRange.GetEnd();
            int       maxDataPoints = configuration.PlotConfig.MaxDataPoints;
            var       listHistories = new List <VTTQs>();

            QualityFilter filter = configuration.PlotConfig.FilterByQuality;

            foreach (var variable in Variables)
            {
                try {
                    VTTQs data = await Connection.HistorianReadRaw(variable, tStart, tEnd, maxDataPoints, BoundingMethod.CompressToN, filter);

                    listHistories.Add(data);
                }
                catch (Exception) {
                    listHistories.Add(new VTTQs());
                }
            }

            IsLoaded = true;

            var(windowLeft, windowRight) = GetTimeWindow(timeRange, listHistories);

            dataRevision += 1;

            var res = MemoryManager.GetMemoryStream("LoadHistory");

            try {
                using (var writer = new StreamWriter(res, Encoding.ASCII, 8 * 1024, leaveOpen: true)) {
                    writer.Write("{ \"WindowLeft\": " + windowLeft.JavaTicks);
                    writer.Write(", \"WindowRight\": " + windowRight.JavaTicks);
                    writer.Write(", \"DataRevision\": " + dataRevision);
                    writer.Write(", \"Data\": ");
                    WriteUnifiedData(new JsonDataRecordArrayWriter(writer), listHistories);
                    writer.Write('}');
                }
                res.Seek(0, SeekOrigin.Begin);
            }
            catch (Exception) {
                res.Dispose();
                throw;
            }
            return(new ReqResult(200, res));
        }
Beispiel #6
0
        public static List <ItemModel> ApplyFilter(List <IBaseFilter> filters, List <QualityModel> qualities)
        {
            var items = new ItemReadLogic().ReadItems();

            foreach (var filter in filters)
            {
                items = filter.GetUnfilteredItems(items);
            }

            var qualityFilter = new QualityFilter();

            qualityFilter.Qualities = qualities;
            items = qualityFilter.GetUnfilteredItems(items);

            return(items);
        }
Beispiel #7
0
 public abstract long CountData(Timestamp startInclusive, Timestamp endInclusive, QualityFilter filter);
Beispiel #8
0
 public abstract List <VTTQ> ReadData(Timestamp startInclusive, Timestamp endInclusive, int maxValues, BoundingMethod bounding, QualityFilter filter);
Beispiel #9
0
 internal static QualityFilterHelper Make(QualityFilter filter)
 {
     return(new QualityFilterHelper(filter));
 }
Beispiel #10
0
 private QualityFilterHelper(QualityFilter filter)
 {
     IncludeAll       = filter == QualityFilter.ExcludeNone;
     IncludeUncertain = filter != QualityFilter.ExcludeNonGood;
 }
Beispiel #11
0
        public async Task <long> HistorianCount(VariableRef variable, Timestamp startInclusive, Timestamp endInclusive, QualityFilter filter)
        {
            HistoryDBWorker?worker = WorkerByVarRef(variable);

            if (worker == null)
            {
                throw new Exception("Failed to find DB worker for variable " + variable);
            }
            CheckExistingVariable(variable);
            return(await worker.Count(variable, startInclusive, endInclusive, filter));
        }
        public override long CountData(Timestamp startInclusive, Timestamp endInclusive, QualityFilter filter)
        {
            PreparedStatement stmt = stmtCountAllQuality;

            if (filter == QualityFilter.ExcludeBad)
            {
                stmt = stmtCountNonBad;
            }
            else if (filter == QualityFilter.ExcludeNonGood)
            {
                stmt = stmtCountGood;
            }
            stmt[0] = startInclusive.ToDateTime();
            stmt[1] = endInclusive.ToDateTime();
            return((long)stmt.ExecuteScalar());
        }
        public Task <long> Count(VariableRef variable, Timestamp startInclusive, Timestamp endInclusive, QualityFilter filter)
        {
            var promise = new TaskCompletionSource <long>();

            if (CheckPrecondition(promise))
            {
                queue.Post(new WI_Count(variable, startInclusive, endInclusive, filter, promise));
            }
            return(promise.Task);
        }
        public Task <List <VTTQ> > ReadRaw(VariableRef variable, Timestamp startInclusive, Timestamp endInclusive, int maxValues, BoundingMethod bounding, QualityFilter filter)
        {
            var promise = new TaskCompletionSource <List <VTTQ> >();

            if (CheckPrecondition(promise))
            {
                queue.Post(new WI_ReadRaw(variable, startInclusive, endInclusive, maxValues, bounding, filter, promise));
            }
            return(promise.Task);
        }
Beispiel #15
0
        public async Task <ReqResult> UiReq_DownloadFile(
            TimeRange timeRange,
            VariableRef[] variables,
            string[] variableNames,
            FileType fileType)
        {
            QualityFilter filter = configuration.PlotConfig.FilterByQuality;

            Timestamp tStart = timeRange.GetStart();
            Timestamp tEnd   = timeRange.GetEnd();

            var listHistories = new List <VTTQs>();

            foreach (var variable in variables)
            {
                try {
                    const int ChunckSize = 5000;
                    VTTQs     data       = await Connection.HistorianReadRaw(variable, tStart, tEnd, ChunckSize, BoundingMethod.TakeFirstN, filter);

                    if (data.Count < ChunckSize)
                    {
                        listHistories.Add(data);
                    }
                    else
                    {
                        var buffer = new List <VTTQ>(data);
                        do
                        {
                            Timestamp t = data[data.Count - 1].T.AddMillis(1);
                            data = await Connection.HistorianReadRaw(variable, t, tEnd, ChunckSize, BoundingMethod.TakeFirstN, filter);

                            buffer.AddRange(data);
                        }while (data.Count == ChunckSize);

                        listHistories.Add(buffer);
                    }
                }
                catch (Exception) {
                    listHistories.Add(new VTTQs());
                }
            }

            var columns = new List <string>();

            columns.Add("Time");
            columns.AddRange(variableNames);

            var res = MemoryManager.GetMemoryStream("DownloadFile");

            try {
                string contentType;
                switch (fileType)
                {
                case FileType.CSV:

                    contentType = "text/csv";
                    using (var writer = new StreamWriter(res, Encoding.UTF8, 8 * 1024, leaveOpen: true)) {
                        WriteUnifiedData(new CsvDataRecordArrayWriter(writer, columns, configuration.DataExport.CSV), listHistories);
                    }
                    break;

                case FileType.Spreadsheet:

                    contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                    using (var excel = new ExcelPackage(res)) {
                        ExcelWorksheet sheet = excel.Workbook.Worksheets.Add("Data Export");
                        WriteUnifiedData(new ExcelDataRecordArrayWriter(sheet, columns, configuration.DataExport.Spreadsheet), listHistories);
                        excel.Save();
                    }
                    break;

                default: throw new Exception($"Unknown file type: {fileType}");
                }

                res.Seek(0, SeekOrigin.Begin);
                return(new ReqResult(200, res, contentType));
            }
            catch (Exception) {
                res.Dispose();
                throw;
            }
        }
Beispiel #16
0
        public override List <VTTQ> ReadData(Timestamp startInclusive, Timestamp endInclusive, int maxValues, BoundingMethod bounding, QualityFilter filter)
        {
            long N = CountData(startInclusive, endInclusive, filter);

            //double millis = (endInclusive - startInclusive).TotalMilliseconds + 1;
            //double avgInterval_MS = millis / N;
            //double requestInterval_MS = 60 * 1000;

            //double N_2 = N;
            //if (requestInterval_MS > avgInterval_MS) {
            //    N_2 = N / (requestInterval_MS / avgInterval_MS);
            //}

            PreparedStatement statement;

            switch (bounding)
            {
            case BoundingMethod.TakeFirstN:
                statement = stmtRawFirstN;
                break;

            case BoundingMethod.TakeLastN:
                statement = stmtRawLastN;
                break;

            case BoundingMethod.CompressToN:
                if (N <= maxValues)
                {
                    return(ReadData(startInclusive, endInclusive, maxValues, BoundingMethod.TakeFirstN, filter));
                }
                else
                {
                    return(ReadDataCompressed(startInclusive, endInclusive, maxValues, N, filter));
                }

            default:
                throw new Exception($"Unknown BoundingMethod: {bounding}");
            }

            statement[0] = startInclusive.JavaTicks;
            statement[1] = endInclusive.JavaTicks;
            statement[2] = maxValues;

            var filterHelper = QualityFilterHelper.Make(filter);

            int initSize = N < maxValues ? (int)N : maxValues;
            var res      = new List <VTTQ>(initSize);

            using (var reader = statement.ExecuteReader()) {
                while (reader.Read())
                {
                    VTTQ vttq = ReadVTTQ(reader);
                    if (filterHelper.Include(vttq.Q))
                    {
                        res.Add(vttq);
                    }
                }
            }

            if (bounding == BoundingMethod.TakeLastN)
            {
                res.Reverse();
            }

            return(res);
        }
        public override List <VTTQ> ReadData(Timestamp startInclusive, Timestamp endInclusive, int maxValues, BoundingMethod bounding, QualityFilter filter)
        {
            long N = CountData(startInclusive, endInclusive, filter);

            PreparedStatement statement;

            switch (bounding)
            {
            case BoundingMethod.TakeFirstN:
                statement = stmtRawFirstN;
                break;

            case BoundingMethod.TakeLastN:
                statement = stmtRawLastN;
                break;

            case BoundingMethod.CompressToN:
                if (N <= maxValues)
                {
                    return(ReadData(startInclusive, endInclusive, maxValues, BoundingMethod.TakeFirstN, filter));
                }
                else
                {
                    return(ReadDataCompressed(startInclusive, endInclusive, maxValues, N, filter));
                }

            default:
                throw new Exception($"Unknown BoundingMethod: {bounding}");
            }

            statement[0] = startInclusive.ToDateTime();
            statement[1] = endInclusive.ToDateTime();
            statement[2] = maxValues;

            var filterHelper = QualityFilterHelper.Make(filter);

            int initSize = N < maxValues ? (int)N : maxValues;
            var res      = new List <VTTQ>(initSize);

            using (var reader = statement.ExecuteReader()) {
                while (reader.Read())
                {
                    VTTQ vttq = ReadVTTQ(reader);
                    if (filterHelper.Include(vttq.Q))
                    {
                        res.Add(vttq);
                    }
                }
            }

            if (bounding == BoundingMethod.TakeLastN)
            {
                res.Reverse();
            }

            return(res);
        }
Beispiel #18
0
        public override async Task <VTTQs> HistorianReadRaw(VariableRef variable, Timestamp startInclusive, Timestamp endInclusive, int maxValues, BoundingMethod bounding, QualityFilter filter = QualityFilter.ExcludeNone)
        {
            var request = MakeSessionRequest <HistorianReadRawReq>();

            request.Variable       = variable;
            request.StartInclusive = startInclusive;
            request.EndInclusive   = endInclusive;
            request.MaxValues      = maxValues;
            request.Bounding       = bounding;
            request.Filter         = filter;
            return(await Post <VTTQs>(request, binaryDeserializer : BinSeri.VTTQ_Serializer.Deserialize));
        }
        private List <VTTQ> ReadDataCompressed(Timestamp startInclusive, Timestamp endInclusive, int maxValues, long count, QualityFilter filter)
        {
            PreparedStatement statement = stmtRawFirst;

            statement[0] = startInclusive.ToDateTime();
            statement[1] = endInclusive.ToDateTime();

            var result = new List <VTTQ>(maxValues);

            int maxIntervals     = maxValues / 3;
            int itemsPerInterval = (maxValues < 6) ? (int)count : (int)Math.Ceiling(((double)count) / maxIntervals);
            var buffer           = new List <VTTQ_D>(itemsPerInterval);

            var filterHelper = QualityFilterHelper.Make(filter);

            using (var reader = statement.ExecuteReader()) {
                while (reader.Read())
                {
                    VTTQ x = ReadVTTQ(reader);
                    if (!x.V.IsEmpty)
                    {
                        double?value = x.V.AsDouble();
                        if (value.HasValue && filterHelper.Include(x.Q))
                        {
                            buffer.Add(new VTTQ_D(x, value.Value));
                        }
                    }
                    if (buffer.Count >= itemsPerInterval)
                    {
                        FlushBuffer(result, buffer, maxValues);
                    }
                }
            }

            if (buffer.Count > 0)
            {
                FlushBuffer(result, buffer, maxValues);
            }

            return(result);
        }
 public override Task <List <VTTQ> > HistorianReadRaw(VariableRef variable, Timestamp startInclusive, Timestamp endInclusive, int maxValues, BoundingMethod bounding, QualityFilter filter = QualityFilter.ExcludeNone)
 {
     throw new InvalidOperationException("HistorianReadRaw on closed connection");
 }
Beispiel #21
0
        public override async Task <long> HistorianCount(VariableRef variable, Timestamp startInclusive, Timestamp endInclusive, QualityFilter filter = QualityFilter.ExcludeNone)
        {
            var request = MakeSessionRequest <HistorianCountReq>();

            request.Variable       = variable;
            request.StartInclusive = startInclusive;
            request.EndInclusive   = endInclusive;
            request.Filter         = filter;
            return(await Post <long>(request));
        }
Beispiel #22
0
        public async Task <List <VTTQ> > HistorianReadRaw(VariableRef variable, Timestamp startInclusive, Timestamp endInclusive, int maxValues, BoundingMethod bounding, QualityFilter filter)
        {
            HistoryDBWorker?worker = WorkerByVarRef(variable);

            if (worker == null)
            {
                throw new Exception("Failed to find DB worker for variable " + variable);
            }
            CheckExistingVariable(variable);
            return(await worker.ReadRaw(variable, startInclusive, endInclusive, maxValues, bounding, filter));
        }
Beispiel #23
0
        public override async Task <ReqResult> OnUiRequestAsync(string command, DataValue parameters)
        {
            switch (command)
            {
            case "Init": {
                var para = parameters.Object <InitParams>() ?? throw new Exception("InitParams is null");
                var(windowLeft, windowRight) = GetTimeWindow(para.TimeRange, new List <VTTQs>());

                var res = new LoadHistoryResult();
                res.Tabs = configuration.Tabs.Select(t => new TabRes()
                    {
                        Name          = t.Name,
                        MaxDataPoints = t.PlotConfig.MaxDataPoints,
                        Variables     = t.Items.Select(it => it.Variable).ToArray(),
                        Options       = MakeGraphOptions(t),
                        Configuration = t
                    }).ToArray();

                res.WindowLeft  = windowLeft.JavaTicks;
                res.WindowRight = windowRight.JavaTicks;

                tabStates = GetInitialTabStates(configuration);

                ObjectRef[] objects = configuration.Tabs.SelectMany(t => t.Items.Select(it => it.Variable.Object)).Distinct().ToArray();
                ObjectInfos infos;
                try {
                    infos = await Connection.GetObjectsByID(objects);
                }
                catch (Exception) {
                    infos = new ObjectInfos(objects.Length);
                    for (int i = 0; i < objects.Length; ++i)
                    {
                        ObjectRef obj = objects[i];
                        try {
                            infos.Add(await Connection.GetObjectByID(obj));
                        }
                        catch (Exception) {
                            infos.Add(new ObjectInfo(obj, "???", "???"));
                        }
                    }
                }

                foreach (ObjectInfo info in infos)
                {
                    var numericVariables = info.Variables.Where(IsNumericOrBool).Select(v => v.Name).ToArray();
                    res.ObjectMap[info.ID.ToEncodedString()] = new ObjInfo()
                    {
                        Name      = info.Name,
                        Variables = numericVariables
                    };
                }

                res.Modules = modules;

                await EnableEvents(configuration);

                return(ReqResult.OK(res));
            }

            case "LoadTabData": {
                var para = parameters.Object <LoadHistoryParams>() ?? throw new Exception("LoadHistoryParams is null");

                TabState tabState = tabStates.FirstOrDefault(ts => ts.TabName == para.TabName);
                if (tabState == null)
                {
                    throw new Exception($"Failed to load history data: tab '{para.TabName}' not found");
                }

                tabState.LastTimeRange = para.TimeRange;

                Timestamp tStart = para.TimeRange.GetStart();
                Timestamp tEnd   = para.TimeRange.GetEnd();

                TabConfig     tabConfig = configuration.Tabs.First(t => t.Name == para.TabName);
                QualityFilter filter    = tabConfig.PlotConfig.FilterByQuality;

                var listHistories = new List <VTTQs>();

                foreach (var variable in para.Variables)
                {
                    try {
                        VTTQs data = await Connection.HistorianReadRaw(variable, tStart, tEnd, para.MaxDataPoints, BoundingMethod.CompressToN, filter);

                        listHistories.Add(data);
                    }
                    catch (Exception) {
                        listHistories.Add(new VTTQs());
                    }
                }

                tabState.IsLoaded = true;

                var(windowLeft, windowRight) = GetTimeWindow(para.TimeRange, listHistories);

                var res = MemoryManager.GetMemoryStream("LoadHistory");
                try {
                    using (var writer = new StreamWriter(res, Encoding.ASCII, 8 * 1024, leaveOpen: true)) {
                        writer.Write("{ \"WindowLeft\": " + windowLeft.JavaTicks);
                        writer.Write(", \"WindowRight\": " + windowRight.JavaTicks);
                        writer.Write(", \"Data\": ");
                        WriteUnifiedData(new JsonDataRecordArrayWriter(writer), listHistories);
                        writer.Write('}');
                    }
                    res.Seek(0, SeekOrigin.Begin);
                }
                catch (Exception) {
                    res.Dispose();
                    throw;
                }
                return(new ReqResult(200, res));
            }

            case "DownloadFile": {
                var           para      = parameters.Object <DownloadDataFileParams>() ?? throw new Exception("DownloadDataFileParams is null");
                TabConfig     tabConfig = configuration.Tabs.First(t => t.Name == para.TabName);
                QualityFilter filter    = tabConfig.PlotConfig.FilterByQuality;

                Timestamp tStart = para.TimeRange.GetStart();
                Timestamp tEnd   = para.TimeRange.GetEnd();

                var listHistories = new List <VTTQs>();

                foreach (var variable in para.Variables)
                {
                    try {
                        const int ChunckSize = 5000;
                        VTTQs     data       = await Connection.HistorianReadRaw(variable, tStart, tEnd, ChunckSize, BoundingMethod.TakeFirstN, filter);

                        if (data.Count < ChunckSize)
                        {
                            listHistories.Add(data);
                        }
                        else
                        {
                            var buffer = new VTTQs(data);
                            do
                            {
                                Timestamp t = data[data.Count - 1].T.AddMillis(1);
                                data = await Connection.HistorianReadRaw(variable, t, tEnd, ChunckSize, BoundingMethod.TakeFirstN, filter);

                                buffer.AddRange(data);
                            }while (data.Count == ChunckSize);

                            listHistories.Add(buffer);
                        }
                    }
                    catch (Exception) {
                        listHistories.Add(new VTTQs());
                    }
                }

                var columns = new List <string>();
                columns.Add("Time");
                columns.AddRange(para.VariableNames);

                var res = MemoryManager.GetMemoryStream("DownloadFile");
                try {
                    string contentType;
                    switch (para.FileType)
                    {
                    case FileType.CSV:

                        contentType = "text/csv";
                        using (var writer = new StreamWriter(res, Encoding.UTF8, 8 * 1024, leaveOpen: true)) {
                            WriteUnifiedData(new CsvDataRecordArrayWriter(writer, columns, configuration.DataExport.CSV), listHistories);
                        }
                        break;

                    case FileType.Spreadsheet:

                        contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                        using (var excel = new ExcelPackage(res)) {
                            ExcelWorksheet sheet = excel.Workbook.Worksheets.Add("Data Export");
                            WriteUnifiedData(new ExcelDataRecordArrayWriter(sheet, columns, configuration.DataExport.Spreadsheet), listHistories);
                            excel.Save();
                        }
                        break;

                    default: throw new Exception($"Unknown file type: {para.FileType}");
                    }

                    res.Seek(0, SeekOrigin.Begin);
                    return(new ReqResult(200, res, contentType));
                }
                catch (Exception) {
                    res.Dispose();
                    throw;
                }
            }

            case "SaveItems": {
                var para = parameters.Object <SaveItemsParams>() ?? throw new Exception("SaveItemsParams is null");

                TabState tabState = tabStates.First(ts => ts.TabName == para.TabName);

                VariableRef[] newVariables = para.Items.Select(it => it.Variable).ToArray();
                bool          reloadData   = !Arrays.Equals(newVariables, tabState.Variables);

                tabState.Variables = newVariables;

                TabConfig tabConfig = configuration.Tabs.FirstOrDefault(t => t.Name == para.TabName);
                tabConfig.Items = para.Items;

                DataValue newConfig = ConfigToIndentedDataValue(configuration);

                await Context.SaveViewConfiguration(newConfig);

                if (reloadData)
                {
                    await EnableEvents(configuration);
                }

                return(ReqResult.OK(new {
                        Options = MakeGraphOptions(tabConfig),
                        Variables = newVariables,
                        Configuration = tabConfig,
                        ReloadData = reloadData
                    }));
            }

            case "SavePlot": {
                var para = parameters.Object <SavePlotParams>() ?? throw new Exception("SavePlotParams is null");

                TabConfig tabConfig = configuration.Tabs.First(t => t.Name == para.TabName);

                bool reloadData =
                    tabConfig.PlotConfig.MaxDataPoints != para.Plot.MaxDataPoints ||
                    tabConfig.PlotConfig.FilterByQuality != para.Plot.FilterByQuality ||
                    tabConfig.PlotConfig.LeftAxisLimitY != para.Plot.LeftAxisLimitY ||
                    tabConfig.PlotConfig.RightAxisLimitY != para.Plot.RightAxisLimitY;

                tabConfig.PlotConfig = para.Plot;

                DataValue newConfig = ConfigToIndentedDataValue(configuration);

                await Context.SaveViewConfiguration(newConfig);

                return(ReqResult.OK(new {
                        Options = MakeGraphOptions(tabConfig),
                        Configuration = tabConfig,
                        ReloadData = reloadData
                    }));
            }

            case "DeleteTab": {
                var para = parameters.Object <DeleteTabParams>() ?? throw new Exception("DeleteTabParams is null");

                configuration.Tabs = configuration.Tabs.Where(t => t.Name != para.TabName).ToArray();

                DataValue newConfig = ConfigToIndentedDataValue(configuration);

                await Context.SaveViewConfiguration(newConfig);

                return(ReqResult.OK(""));
            }

            case "RenameTab": {
                var para = parameters.Object <RenameTabParams>() ?? throw new Exception("RenameTabParams is null");

                if (para.NewName != para.TabName && configuration.Tabs.Any(t => t.Name == para.NewName))
                {
                    throw new Exception("Tab name already exists!");
                }

                TabConfig tabConfig = configuration.Tabs.First(t => t.Name == para.TabName);
                tabConfig.Name = para.NewName;

                TabState tabState = tabStates.First(ts => ts.TabName == para.TabName);
                tabState.TabName = para.NewName;

                DataValue newConfig = ConfigToIndentedDataValue(configuration);

                await Context.SaveViewConfiguration(newConfig);

                return(ReqResult.OK(new {
                        Configuration = tabConfig
                    }));
            }

            case "AddTab": {
                var para = parameters.Object <AddTabParams>() ?? throw new Exception("AddTabParams is null");
                var(windowLeft, windowRight) = GetTimeWindow(para.TimeRange, new List <VTTQs>());

                if (configuration.Tabs.Any(t => t.Name == para.NewName))
                {
                    throw new Exception("Tab name already exists!");
                }

                TabConfig tabConfig = new TabConfig();
                tabConfig.Name = para.NewName;

                var tabs = configuration.Tabs.ToList();
                tabs.Add(tabConfig);
                configuration.Tabs = tabs.ToArray();

                DataValue newConfig = ConfigToIndentedDataValue(configuration);

                await Context.SaveViewConfiguration(newConfig);

                var tabState = new TabState()
                {
                    TabName       = para.NewName,
                    LastTimeRange = para.TimeRange
                };
                tabStates.Add(tabState);

                return(ReqResult.OK(new {
                        NewTab = new TabRes()
                        {
                            Name = tabConfig.Name,
                            MaxDataPoints = tabConfig.PlotConfig.MaxDataPoints,
                            Options = MakeGraphOptions(tabConfig),
                            Configuration = tabConfig
                        },
                        WindowLeft = windowLeft.JavaTicks,
                        WindowRight = windowRight.JavaTicks
                    }));
            }

            case "MoveLeft": {
                var para = parameters.Object <MoveTabParams>() ?? throw new Exception("MoveTabParams is null");

                int i = configuration.Tabs.ToList().FindIndex(t => t.Name == para.TabName);
                if (i <= 0)
                {
                    throw new Exception("Can't move left");
                }

                var tmp = configuration.Tabs[i];
                configuration.Tabs[i]     = configuration.Tabs[i - 1];
                configuration.Tabs[i - 1] = tmp;

                DataValue newConfig = ConfigToIndentedDataValue(configuration);

                await Context.SaveViewConfiguration(newConfig);

                return(ReqResult.OK(""));
            }

            case "MoveRight": {
                var para = parameters.Object <MoveTabParams>() ?? throw new Exception("MoveTabParams is null");

                int i = configuration.Tabs.ToList().FindIndex(t => t.Name == para.TabName);
                if (i >= configuration.Tabs.Length - 1)
                {
                    throw new Exception("Can't move right");
                }

                var tmp = configuration.Tabs[i];
                configuration.Tabs[i]     = configuration.Tabs[i + 1];
                configuration.Tabs[i + 1] = tmp;

                DataValue newConfig = ConfigToIndentedDataValue(configuration);

                await Context.SaveViewConfiguration(newConfig);

                return(ReqResult.OK(""));
            }

            case "ReadModuleObjects": {
                var pars = parameters.Object <ReadModuleObjectsParams>() ?? throw new Exception("ReadModuleObjectsParams is null");

                ObjectInfos objects;

                try {
                    objects = await Connection.GetAllObjects(pars.ModuleID);
                }
                catch (Exception) {
                    objects = new ObjectInfos();
                }

                return(ReqResult.OK(new {
                        Items = objects.Where(o => o.Variables.Any(IsNumericOrBool)).Select(o => new Obj()
                        {
                            Type = o.ClassName,
                            ID = o.ID.ToEncodedString(),
                            Name = o.Name,
                            Variables = o.Variables.Where(IsNumericOrBool).Select(v => v.Name).ToArray()
                        }).ToArray()
                    }));
            }

            default:
                return(ReqResult.Bad("Unknown command: " + command));
            }
        }
 public override Task <long> HistorianCount(VariableRef variable, Timestamp startInclusive, Timestamp endInclusive, QualityFilter filter = QualityFilter.ExcludeNone)
 {
     throw new InvalidOperationException("HistorianCount on closed connection");
 }