-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBandActor.cs
More file actions
192 lines (160 loc) · 9.09 KB
/
Copy pathBandActor.cs
File metadata and controls
192 lines (160 loc) · 9.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
namespace HealthMetrics.BandActor
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Fabric;
using System.Fabric.Description;
using System.Linq;
using System.Threading.Tasks;
using HealthMetrics.BandActor.Interfaces;
using HealthMetrics.Common;
using HealthMetrics.DoctorActor.Interfaces;
using Microsoft.ServiceFabric.Actors;
using Microsoft.ServiceFabric.Actors.Client;
using Microsoft.ServiceFabric.Actors.Runtime;
using Microsoft.ServiceFabric.Data;
internal class BandActor : Actor, IBandActor, IRemindable
{
private const string GenerateHealthDataAsyncReminder = "GenerateHealthDataAsync";
private const string GenerateAndSendHealthReportReminder = "SendHealthReportAsync";
private readonly TimeSpan TimeWindow = TimeSpan.FromMinutes(2);
private Uri doctorActorServiceUri;
private CryptoRandom random = new CryptoRandom();
private HealthIndexCalculator indexCalculator;
public BandActor(ActorService actorService, ActorId actorId)
: base(actorService, actorId)
{
}
public async Task<BandDataViewModel> GetBandDataAsync()
{
try
{
//check to see if the patient name is set
//if not this actor object hasn't been initialized
//and we can skip the rest of the checks
ConditionalValue<string> PatientInfoResult = await this.StateManager.TryGetStateAsync<string>("PatientName");
if (PatientInfoResult.HasValue)
{
ConditionalValue<CountyRecord> CountyInfoResult = await this.StateManager.TryGetStateAsync<CountyRecord>("CountyInfo");
ConditionalValue<Guid> DoctorInfoResult = await this.StateManager.TryGetStateAsync<Guid>("DoctorId");
ConditionalValue<HealthIndex> HeatlthInfoResult = await this.StateManager.TryGetStateAsync<HealthIndex>("HealthIndex");
ConditionalValue<List<HeartRateRecord>> HeartRateRecords =
await this.StateManager.TryGetStateAsync<List<HeartRateRecord>>("HeartRateRecords");
HealthIndexCalculator ic = this.indexCalculator;
HealthIndex healthIndex = ic.ComputeIndex(HeatlthInfoResult.Value);
return new BandDataViewModel(
DoctorInfoResult.Value,
this.Id.GetGuidId(),
PatientInfoResult.Value,
CountyInfoResult.Value,
healthIndex,
HeartRateRecords.Value);
}
}
catch (Exception e)
{
throw new ArgumentException(string.Format("Exception inside band actor {0}|{1}|{2}", this.Id, this.Id.Kind, e));
}
throw new ArgumentException(string.Format("No band actor state {0}|{1}", this.Id, this.Id.Kind));
}
// set state for BandActor
// BandInfo variable is passed from BandCreationService
public async Task NewAsync(BandInfo info)
{
await this.StateManager.SetStateAsync<CountyRecord>("CountyInfo", info.CountyInfo);
await this.StateManager.SetStateAsync<Guid>("DoctorId", info.DoctorId);
await this.StateManager.SetStateAsync<HealthIndex>("HealthIndex", info.HealthIndex);
await this.StateManager.SetStateAsync<string>("PatientName", info.PersonName);
await this.StateManager.SetStateAsync<List<HeartRateRecord>>("HeartRateRecords", new List<HeartRateRecord>()); // initially the heart rate records are empty list
await this.RegisterReminders();
ActorEventSource.Current.ActorMessage(this, "Band created. ID: {0}, Name: {1}, Doctor ID: {2}", this.Id, info.PersonName, info.DoctorId);
}
// This ReceiveReminderAsync method will be called by service fabric runtime when the reminder is triggered
async Task IRemindable.ReceiveReminderAsync(string reminderName, byte[] context, TimeSpan dueTime, TimeSpan period)
{
switch (reminderName)
{
case GenerateAndSendHealthReportReminder:
await this.GenerateAndSendHealthReportAsync();
break;
default:
ActorEventSource.Current.Message("Reminder {0} is not implemented on BandActor.", reminderName);
break;
}
return;
}
protected override Task OnActivateAsync()
{
ConfigurationPackage configPackage = this.ActorService.Context.CodePackageActivationContext.GetConfigurationPackageObject("Config");
this.indexCalculator = new HealthIndexCalculator(this.ActorService.Context);
this.UpdateConfigSettings(configPackage.Settings);
this.ActorService.Context.CodePackageActivationContext.ConfigurationPackageModifiedEvent +=
this.CodePackageActivationContext_ConfigurationPackageModifiedEvent;
ActorEventSource.Current.ActorMessage(this, "Band activated. ID: {0}", this.Id);
return Task.FromResult(true);
}
private async Task GenerateAndSendHealthReportAsync()
{
try
{
ConditionalValue<HealthIndex> HeatlthInfoResult = await this.StateManager.TryGetStateAsync<HealthIndex>("HealthIndex");
ConditionalValue<string> PatientInfoResult = await this.StateManager.TryGetStateAsync<string>("PatientName");
ConditionalValue<Guid> DoctorInfoResult = await this.StateManager.TryGetStateAsync<Guid>("DoctorId");
if (HeatlthInfoResult.HasValue && PatientInfoResult.HasValue && DoctorInfoResult.HasValue)
{
ActorId doctorId = new ActorId(DoctorInfoResult.Value);
HeartRateRecord record = new HeartRateRecord((float) this.random.NextDouble()); // generate a heart rate record
await this.SaveHealthDataAsync(record);
IDoctorActor doctor = ActorProxy.Create<IDoctorActor>(doctorId, this.doctorActorServiceUri);
// the health report seems doesn't contain any changing data
await
doctor.ReportHealthAsync(
this.Id.GetGuidId(),
PatientInfoResult.Value,
HeatlthInfoResult.Value);
ActorEventSource.Current.Message("Health info sent from band {0} to doctor {1}", this.Id, DoctorInfoResult.Value);
}
}
catch (Exception e)
{
ActorEventSource.Current.Message(
"Band Actor failed to send health data to doctor. Exception: {0}",
(e is AggregateException) ? e.InnerException.ToString() : e.ToString());
}
return;
}
private void UpdateConfigSettings(ConfigurationSettings configSettings)
{
KeyedCollection<string, ConfigurationProperty> parameters = configSettings.Sections["HealthMetrics.BandActor.Settings"].Parameters;
this.doctorActorServiceUri = new ServiceUriBuilder(parameters["DoctorActorServiceInstanceName"].Value).ToUri();
}
private void CodePackageActivationContext_ConfigurationPackageModifiedEvent(object sender, PackageModifiedEventArgs<ConfigurationPackage> e)
{
this.UpdateConfigSettings(e.NewPackage.Settings);
}
// update the heart rate record state
private async Task SaveHealthDataAsync(HeartRateRecord newRecord)
{
ConditionalValue<List<HeartRateRecord>> HeartRateRecords = await this.StateManager.TryGetStateAsync<List<HeartRateRecord>>("HeartRateRecords");
if (HeartRateRecords.HasValue)
{
List<HeartRateRecord> records = HeartRateRecords.Value;
// keep lastest record within the time window
records = records.Where(x => DateTimeOffset.UtcNow - x.Timestamp.ToUniversalTime() <= this.TimeWindow).ToList();
records.Add(newRecord);
await this.StateManager.SetStateAsync<List<HeartRateRecord>>("HeartRateRecords", records);
}
return;
}
// register a reminder for this actor service
private async Task RegisterReminders()
{
await this.RegisterReminderAsync(GenerateAndSendHealthReportReminder, null, TimeSpan.FromSeconds(this.random.Next(5, 15)), TimeSpan.FromSeconds(5));
}
}
}