Skip to content

DHI.Services.MIKECloud for Time Series — Internal Developer Guide

Use MIKECloud as a Time Series backend. CRUD + values over Timeseries datasets. For platform/auth/path rules, see MIKECloud Core.


What you get

Repository

  • DHI.Services.Provider.MIKECloud.GroupedTimeSeriesRepository Grouped & updatable repo over MIKECloud:
    • Read: list series/groups, fetch entities, values (optionally by range)
    • Write: create series, append/set, remove ranges, delete series

SDK clients under the hood: ProjectClient, DatasetClient, TimeSeriesClient


Identity & path rules

FullName (ID) format:

<dataset>/<timeSeriesName>
<sub1>/<dataset>/<timeSeriesName>
<sub1>/<sub2>/<dataset>/<timeSeriesName>
  • Leading / is allowed; provider trims it.
  • Last segment = time series name; the preceding path resolves a Timeseries dataset.
  • Add: creates missing subprojects (folders); the dataset must already exist (not auto-created).

Groups & listing

  • Datasets are shown as groups and end with / when listed: e.g., /Ops/North/Telemetry/.
  • Append ;nonrecursive to restrict list calls to direct children.

Capabilities & behaviors

Operation / Area Support Notes
Get(series) Populates Unit, Quantity via EUMWrapper; DataType when present
GetValues(series [from,to]) Returns TimeSeriesDataWFlag<double,object>; flags are null
Add(series) Creates MIKECloud time series (EUM-mapped unit/item), then uploads initial values
SetValues(series, data) Appends values; no client-side de-dup
RemoveValues(series, from,to) Deletes values in range (or all with min/max)
Remove(series) Deletes series
Update(series) Current SDK path = delete + recreate (reads existing values, re-adds with new headers/metadata)
GetAll / GetByGroup / GetFullNames Lists across subprojects; datasets & subprojects end with /
Contains(series) Query by Item == name within dataset
ContainsGroup(path) True if dataset or path exists

Type & unit mapping

  • EUM mapping both ways:
    • Write: EUMWrapper.GetUnitTag(unitAbbrev, out unitKey)eumUnit EUMWrapper.GetItemTypeTag(quantityKey, out itemKey)eumItem
    • Read: eumGetUnitAbbreviation, eumGetItemTypeKeyUnit, Quantity
  • TimeSeriesDataType: numeric values align with MIKECloud; semantics (resample, smoothing, etc.) live in your analysis code/Web API.

Time: use UTC for consistency.


Connecting

Direct (code)

var repo = new GroupedTimeSeriesRepository(
  "apiKey=<APIKEY>;projectId=<PROJECTID>;environment=Prod");
var svc = new DHI.Services.TimeSeries.GroupedUpdatableTimeSeriesService<string,double>(repo);
ServiceLocator.Register(svc, "mc-ts");

Web API (Connections module)

{
  "$type": "DHI.Services.TimeSeries.WebApi.GroupedUpdatableTimeSeriesServiceConnection, DHI.Services.TimeSeries.WebApi",
  "RepositoryType": "DHI.Services.Provider.MIKECloud.GroupedTimeSeriesRepository, DHI.Services.Provider.MIKECloud",
  "ConnectionString": "apiKey=[env:MIKECLOUD_APIKEY];projectId=[env:MIKECLOUD_PROJECT];environment=Prod",
  "Name": "Time Series via MIKECloud",
  "Id": "mc-ts"
}

Routes then appear as: /api/timeseries/mc-ts/...


Code recipes

Create, write, read values

var tsRepo = new GroupedTimeSeriesRepository(conn);

var ts = new TimeSeries<string,double>(
  id: null,
  name: "WaterLevel",
  group: "/Hydro/Observations/") {
  Unit = "m",
  Quantity = "WL",
  DataType = TimeSeriesDataType.Instantaneous,
  Data = new TimeSeriesData<double>()
};

ts.Data.Append(new DateTime(2025,9,1,0,0,0, DateTimeKind.Utc), 1.23);
tsRepo.Add(ts);

var values = tsRepo.GetValues("/Hydro/Observations/WaterLevel",
                              new DateTime(2025,9,1), new DateTime(2025,9,30)).Value;

Replace a window (avoid overlaps)

tsRepo.RemoveValues("/Hydro/Observations/WaterLevel",
                    new DateTime(2025,9,2), new DateTime(2025,9,3));
tsRepo.SetValues("/Hydro/Observations/WaterLevel", newData);

Troubleshooting & notes

  • Dataset existence: provider creates subprojects but not datasets. Provision the Timeseries dataset first.
  • Project not found / bad env: constructor throws ArgumentException.
  • Listings: GetAll() & recursive GetFullNames() can span large trees; prefer ;nonrecursive or targeted groups.
  • Update: current path is delete+recreate—if you only change values, prefer RemoveValues + SetValues.