-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
controller state save and restore abillity
- Loading branch information
Showing
11 changed files
with
274 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
using System.Reflection; | ||
|
||
namespace Deployf.Botf; | ||
|
||
public struct BotControllerStateService | ||
{ | ||
public static Dictionary<Type, Func<BotController, Task>?> _savers = new (); | ||
public static Dictionary<Type, Func<BotController, Task>?> _loaders = new (); | ||
|
||
public async Task Load(BotController controller) | ||
{ | ||
var controllerType = controller.GetType(); | ||
if (_loaders.TryGetValue(controllerType, out var loader) && loader != null) | ||
{ | ||
await loader(controller); | ||
return; | ||
} | ||
|
||
List<FieldInfo> fields; | ||
List<PropertyInfo> props; | ||
ExtractMembers(controllerType, out fields, out props); | ||
|
||
if (fields.Count > 0 || props.Count > 0) | ||
{ | ||
loader = async (BotController _controller) => | ||
{ | ||
var storage = _controller.Store!; | ||
foreach (var field in fields) | ||
{ | ||
var value = await storage.Get(_controller.FromId, GetKey(field, controllerType), null); | ||
field.SetValue(_controller, value); | ||
} | ||
foreach (var prop in props) | ||
{ | ||
var value = await storage.Get(_controller.FromId, GetKey(prop, controllerType), null); | ||
prop.SetValue(_controller, value); | ||
} | ||
}; | ||
|
||
_loaders[controllerType] = loader; | ||
|
||
await loader(controller); | ||
} | ||
else | ||
{ | ||
_loaders[controllerType] = null; | ||
} | ||
} | ||
|
||
public async Task Save(BotController controller) | ||
{ | ||
var controllerType = controller.GetType(); | ||
if (_savers.TryGetValue(controllerType, out var saver) && saver != null) | ||
{ | ||
await saver(controller); | ||
return; | ||
} | ||
|
||
List<FieldInfo> fields; | ||
List<PropertyInfo> props; | ||
ExtractMembers(controllerType, out fields, out props); | ||
|
||
if (fields.Count > 0 || props.Count > 0) | ||
{ | ||
saver = async (BotController _controller) => | ||
{ | ||
var storage = _controller.Store!; | ||
foreach (var field in fields) | ||
{ | ||
var value = field.GetValue(_controller); | ||
var key = GetKey(field, controllerType); | ||
if(value != null) | ||
{ | ||
await storage.Set(_controller.FromId, key, value); | ||
} | ||
else | ||
{ | ||
await storage.Remove(_controller.FromId, key); | ||
} | ||
} | ||
foreach (var prop in props) | ||
{ | ||
var value = prop.GetValue(_controller); | ||
var key = GetKey(prop, controllerType); | ||
if(value != null) | ||
{ | ||
await storage.Set(_controller.FromId, key, value); | ||
} | ||
else | ||
{ | ||
await storage.Remove(_controller.FromId, key); | ||
} | ||
} | ||
}; | ||
|
||
_savers[controllerType] = saver; | ||
|
||
await saver(controller); | ||
} | ||
else | ||
{ | ||
_savers[controllerType] = null; | ||
} | ||
} | ||
|
||
static void ExtractMembers(Type controllerType, out List<FieldInfo> fields, out List<PropertyInfo> props) | ||
{ | ||
const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; | ||
|
||
fields = controllerType | ||
.GetFields(bindingFlags) | ||
.Where(c => c.GetCustomAttribute<StateAttribute>() != null) | ||
.ToList(); | ||
props = controllerType | ||
.GetProperties(bindingFlags) | ||
.Where(c => c.GetCustomAttribute<StateAttribute>() != null) | ||
.ToList(); | ||
} | ||
|
||
static string GetKey(MemberInfo member, Type controllerType) | ||
{ | ||
return $"$ctrl-state_{controllerType.Name}.{member.Name}"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
Examples/Deployf.Botf.ControllerStateExample/Deployf.Botf.ControllerStateExample.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<Nullable>enable</Nullable> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\Deployf.Botf\Deployf.Botf.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
using Deployf.Botf; | ||
|
||
BotfProgram.StartBot(args); | ||
|
||
class ControllerStateExample : BotController | ||
{ | ||
[State] | ||
ExampleClass _data; | ||
|
||
[State] | ||
int intField; | ||
|
||
int nonStateIntField; | ||
|
||
[Action("/start", "start the bot")] | ||
public void Start() | ||
{ | ||
PushL($"Hello!"); | ||
PushL("This is an example of how to store controllers state(fields and props) through updates"); | ||
PushL("Current controller state:"); | ||
DumpState(); | ||
|
||
PushL(); | ||
PushL("For refresh call /start"); | ||
|
||
RowButton("Set random _data", Q(SetRandom_data)); | ||
RowButton("Set random intField", Q(SetRandom_intField)); | ||
RowButton("Set random nonStateIntField", Q(SetRandom_nonStateIntField)); | ||
} | ||
|
||
[Action] | ||
void SetRandom_data() | ||
{ | ||
_data = new ExampleClass() | ||
{ | ||
IntField = Random.Shared.Next(), | ||
StringProp = Guid.NewGuid().ToString() | ||
}; | ||
Start(); | ||
} | ||
|
||
[Action] | ||
void SetRandom_intField() | ||
{ | ||
intField = Random.Shared.Next(); | ||
Start(); | ||
} | ||
|
||
[Action] | ||
void SetRandom_nonStateIntField() | ||
{ | ||
nonStateIntField = Random.Shared.Next(); | ||
Start(); | ||
} | ||
|
||
void DumpState() | ||
{ | ||
if(_data == null) | ||
{ | ||
PushL("_data is null"); | ||
} | ||
else | ||
{ | ||
PushL($"_data is {_data.ToString()}"); | ||
} | ||
|
||
PushL($"intField is {intField}"); | ||
PushL($"nonStateIntField is {nonStateIntField}"); | ||
} | ||
} | ||
|
||
class ExampleClass | ||
{ | ||
public int IntField; | ||
public string StringProp { get; set; } | ||
|
||
public override string ToString() => $"({IntField}, {StringProp})"; | ||
} |
13 changes: 13 additions & 0 deletions
13
Examples/Deployf.Botf.ControllerStateExample/Properties/launchSettings.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"profiles": { | ||
"Deployf.Botf.HelloExample": { | ||
"commandName": "Project", | ||
"dotnetRunMessages": true, | ||
"launchBrowser": false, | ||
"applicationUrl": "http://localhost:5281", | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development" | ||
} | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
Examples/Deployf.Botf.ControllerStateExample/appsettings.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"Logging": { | ||
"LogLevel": { | ||
"Default": "Information", | ||
"Microsoft.AspNetCore": "Warning" | ||
} | ||
}, | ||
"AllowedHosts": "*" | ||
} |