Skip to content

Commit

Permalink
RPC support GetAlarm.
Browse files Browse the repository at this point in the history
  • Loading branch information
hcoona committed Dec 13, 2019
1 parent 0d4b9af commit 1874a67
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ public FakePlc(byte[] id)
WaterPumpFrequencyHertz = 29F,
};

public Alarm Alarm { get; } = new Alarm
{
LowFlowRate = false,
HighHeaterPressure = false,
LowHeaterPressure = false,
NoPower = false,
HeaterOverloadedBroken = false,
ElectricalHeaterBorken = false,
};

public void Dispose()
{
this.Dispose(true);
Expand Down Expand Up @@ -168,6 +178,9 @@ private void BackgroundTaskEntryPoint()
this.UpdateRunningParameter(content);
responseFrame = this.CreateGetRunningParameterResponseFrame();
break;
case PlcMessageType.GetAlarmRequest:
responseFrame = this.CreateGetAlarmResponseFrame();
break;
default:
responseFrame = null;
break;
Expand Down Expand Up @@ -315,5 +328,20 @@ private PlcFrame CreateGetRunningParameterResponseFrame()
PlcMessageType.GetRunningParameterResponse,
ByteString.CopyFrom(responseContent));
}

private PlcFrame CreateGetAlarmResponseFrame()
{
byte[] responseContent = new byte[0x06];
responseContent[1] = (byte)(this.Alarm.LowFlowRate ? 0x01 : 0x00);
responseContent[2] = (byte)(this.Alarm.HighHeaterPressure ? 0x01 : 0x00);
responseContent[3] = (byte)(this.Alarm.LowHeaterPressure ? 0x01 : 0x00);
responseContent[4] = (byte)(this.Alarm.NoPower ? 0x01 : 0x00);
responseContent[5] = (byte)(this.Alarm.HeaterOverloadedBroken ? 0x01 : 0x00);
responseContent[6] = (byte)(this.Alarm.ElectricalHeaterBorken ? 0x01 : 0x00);

return PlcFrame.Create(
PlcMessageType.GetAlarmResponse,
ByteString.CopyFrom(responseContent));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,36 @@ public async Task<WorkingMode> UpdateWorkingModeAsync(UpdateWorkingModeRequest r
};
}

public async Task<Alarm> GetAlarmAsync(GetAlarmRequest request, DateTime? deadline)
{
if (request is null)
{
throw new ArgumentNullException(nameof(request));
}

PlcFrame response = await this.InvokeAsync(
PlcFrame.Create(PlcMessageType.GetAlarmRequest, ByteString.Empty),
deadline)
.ConfigureAwait(false);
if (response.FrameHeader.MessageType != PlcMessageType.GetAlarmResponse)
{
throw new InvalidDataException(
"Response message type mismatch: " + response.FrameHeader.MessageType);
}

using var reader = new BinaryReader(new MemoryStream(response.FrameBody.ToByteArray()));
return new Alarm
{
CreateTime = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow),
LowFlowRate = reader.ReadByte() != 0,
HighHeaterPressure = reader.ReadByte() != 0,
LowHeaterPressure = reader.ReadByte() != 0,
NoPower = reader.ReadByte() != 0,
HeaterOverloadedBroken = reader.ReadByte() != 0,
ElectricalHeaterBorken = reader.ReadByte() != 0,
};
}

public async Task<RunningParameter> GetRunningParameterAsync(
GetRunningParameterRequest request,
DateTime? deadline)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// </copyright>

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
Expand All @@ -20,7 +19,9 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using GrpcAlarm = GeothermalResearchInstitute.v2.Alarm;
using GrpcMetric = GeothermalResearchInstitute.v2.Metric;
using ModelAlarm = GeothermalResearchInstitute.ServerConsole.Models.Alarm;
using ModelMetric = GeothermalResearchInstitute.ServerConsole.Models.Metric;

namespace GeothermalResearchInstitute.ServerConsole.GrpcServices
Expand All @@ -32,7 +33,8 @@ public class DeviceServiceImpl : DeviceService.DeviceServiceBase, IDisposable
private readonly ILogger<DeviceServiceImpl> logger;
private readonly IServiceProvider serviceProvider;
private readonly PlcManager plcManager;
private readonly Timer timer;
private readonly Timer askAlarmTimer;
private readonly Timer askMetricTimer;
private bool disposedValue = false;

public DeviceServiceImpl(
Expand All @@ -43,7 +45,8 @@ public DeviceServiceImpl(
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
this.plcManager = plcManager ?? throw new ArgumentNullException(nameof(plcManager));
this.timer = new Timer(this.AskPersistDeviceMetricAlarm, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10));
this.askAlarmTimer = new Timer(this.AskPersistDeviceAlarm, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10));
this.askMetricTimer = new Timer(this.AskPersistDeviceMetric, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10));

if (this.logger.IsEnabled(LogLevel.Debug))
{
Expand Down Expand Up @@ -96,6 +99,16 @@ public override Task<AuthenticateResponse> Authenticate(
}
}

public override Task<TestResponse> Test(TestRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "Not supported."));
}

public override Task<ConnectResponse> Connect(ConnectRequest request, ServerCallContext context)
{
throw new RpcException(new Status(StatusCode.Unimplemented, "Not supported."));
}

public override Task<ListDevicesResponse> ListDevices(ListDevicesRequest request, ServerCallContext context)
{
IOptionsSnapshot<DeviceOptions> deviceOptions =
Expand Down Expand Up @@ -171,7 +184,7 @@ public override Task<ListMetricsResponse> ListMetrics(ListMetricsRequest request
using (BjdireContext db = this.serviceProvider.GetRequiredService<BjdireContext>())
{
var metrics = (from m in db.Metrics
where m.Id == id
where m.DeviceId == id
&& (startDateTime == null || startDateTime <= m.Timestamp)
&& m.Timestamp <= endDateTime
orderby m.Timestamp descending
Expand Down Expand Up @@ -238,13 +251,30 @@ public override Task<RunningParameter> UpdateRunningParameter(
context);
}

public override Task<GrpcAlarm> GetAlarm(GetAlarmRequest request, ServerCallContext context)
{
return this.Invoke(
(client, request, deadline) => client.GetAlarmAsync(request, deadline),
request.DeviceId,
request,
context);
}

public override Task<ListAlarmChangesResponse> ListAlarmChanges(
ListAlarmChangesRequest request,
ServerCallContext context)
{
throw new NotImplementedException();
}

protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
this.timer.Dispose();
this.askAlarmTimer.Dispose();
this.askMetricTimer.Dispose();
}

this.disposedValue = true;
Expand Down Expand Up @@ -272,7 +302,7 @@ private Task<TResponse> Invoke<TRequest, TResponse>(
}
}

private async void AskPersistDeviceMetricAlarm(object state)
private async void AskPersistDeviceAlarm(object state)
{
IOptionsSnapshot<DeviceOptions> deviceOptions =
this.serviceProvider.GetRequiredService<IOptionsSnapshot<DeviceOptions>>();
Expand All @@ -286,33 +316,76 @@ private async void AskPersistDeviceMetricAlarm(object state)
byte[] id = d.ComputeIdBinary();
if (this.plcManager.PlcDictionary.TryGetValue(ByteString.CopyFrom(id), out PlcClient client))
{
this.logger.LogInformation("Ask metric & alarm for {0}({1})", d.Id, d.Name);
this.logger.LogInformation("Ask alarm for {0}({1})", d.Id, d.Name);

GrpcAlarm alarm = await client
.GetAlarmAsync(new GetAlarmRequest(), DateTime.UtcNow.AddSeconds(5))
.ConfigureAwait(true);

var m = new ModelAlarm
{
DeviceId = BitConverter.ToString(id),
};
alarm.AssignTo(m);

db.Alarms.Add(m);
}
else
{
this.logger.LogWarning(
"Failed to ask alarm for {0}({1}), currently offline.",
d.Id,
d.Name);
}
}
catch (RpcException e)
{
this.logger.LogWarning(e, "Failed to ask alarm for {0}({1})", d.Id, d.Name);
}
}

db.SaveChanges();
}

private async void AskPersistDeviceMetric(object state)
{
IOptionsSnapshot<DeviceOptions> deviceOptions =
this.serviceProvider.GetRequiredService<IOptionsSnapshot<DeviceOptions>>();

using BjdireContext db = this.serviceProvider.GetRequiredService<BjdireContext>();

foreach (DeviceOptionsEntry d in deviceOptions.Value.Devices)
{
try
{
byte[] id = d.ComputeIdBinary();
if (this.plcManager.PlcDictionary.TryGetValue(ByteString.CopyFrom(id), out PlcClient client))
{
this.logger.LogInformation("Ask metric for {0}({1})", d.Id, d.Name);

GrpcMetric metric = await client
.GetMetricAsync(new GetMetricRequest(), DateTime.UtcNow.AddSeconds(5))
.ConfigureAwait(true);

var m = new ModelMetric
{
Id = BitConverter.ToString(id),
DeviceId = BitConverter.ToString(id),
};
metric.AssignTo(m);

db.Metrics.Add(m);

// TODO(zhangshuai.ustc): Persist alarm.
}
else
{
this.logger.LogWarning(
"Failed to ask metric & alarm for {0}({1}), currently offline.",
"Failed to ask metric for {0}({1}), currently offline.",
d.Id,
d.Name);
}
}
catch (RpcException e)
{
this.logger.LogWarning(e, "Failed to ask metric & alarm for {0}({1})", d.Id, d.Name);
this.logger.LogWarning(e, "Failed to ask metric for {0}({1})", d.Id, d.Name);
}
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="20191212150337_InitialCreate.cs" company="Shuai Zhang">
// <copyright file="20191213144330_InitialCreate.cs" company="Shuai Zhang">
// Copyright Shuai Zhang. All rights reserved.
// Licensed under the GPLv3 license. See LICENSE file in the project root for full license information.
// </copyright>
Expand All @@ -17,11 +17,29 @@ protected override void Up(MigrationBuilder migrationBuilder)
throw new ArgumentNullException(nameof(migrationBuilder));
}

migrationBuilder.CreateTable(
name: "Alarms",
columns: table => new
{
DeviceId = table.Column<string>(nullable: false),
Timestamp = table.Column<DateTimeOffset>(nullable: false),
LowFlowRate = table.Column<bool>(nullable: false),
HighHeaterPressure = table.Column<bool>(nullable: false),
LowHeaterPressure = table.Column<bool>(nullable: false),
NoPower = table.Column<bool>(nullable: false),
HeaterOverloadedBroken = table.Column<bool>(nullable: false),
ElectricalHeaterBorken = table.Column<bool>(nullable: false),
},
constraints: table =>
{
table.PrimaryKey("PK_Alarms", x => new { x.DeviceId, x.Timestamp });
});

migrationBuilder.CreateTable(
name: "Metrics",
columns: table => new
{
Id = table.Column<string>(nullable: false),
DeviceId = table.Column<string>(nullable: false),
Timestamp = table.Column<DateTimeOffset>(nullable: false),
OutputWaterCelsiusDegree = table.Column<float>(nullable: false),
InputWaterCelsiusDegree = table.Column<float>(nullable: false),
Expand All @@ -34,7 +52,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
},
constraints: table =>
{
table.PrimaryKey("PK_Metrics", x => new { x.Id, x.Timestamp });
table.PrimaryKey("PK_Metrics", x => new { x.DeviceId, x.Timestamp });
});
}

Expand All @@ -45,6 +63,9 @@ protected override void Down(MigrationBuilder migrationBuilder)
throw new ArgumentNullException(nameof(migrationBuilder));
}

migrationBuilder.DropTable(
name: "Alarms");

migrationBuilder.DropTable(
name: "Metrics");
}
Expand Down
Loading

0 comments on commit 1874a67

Please sign in to comment.