Skip to content

Commit 02beac1

Browse files
authored
Add multiple geofence support per instance (#28)
* Add multiple geofence support per instance * Drop geofence foreign key * Update old geofence values to list
1 parent 927ed61 commit 02beac1

File tree

10 files changed

+89
-54
lines changed

10 files changed

+89
-54
lines changed

migrations/4.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
ALTER TABLE `instance`
2+
DROP FOREIGN KEY `fk_geofence_name`;
3+
4+
ALTER TABLE `instance`
5+
CHANGE `geofence` `geofences` longtext NOT NULL;
6+
7+
UPDATE `instance`
8+
SET geofences = CONCAT("[\"", geofences, "\"]");

src/Chuck.Data/Contexts/DeviceControllerContext.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
5858
modelBuilder.Entity<Instance>()
5959
.Property(nameof(Instance.Data))
6060
.HasConversion(DbContextFactory.CreateJsonValueConverter<InstanceData>());
61+
modelBuilder.Entity<Instance>()
62+
.Property(nameof(Instance.Geofences))
63+
.HasConversion(DbContextFactory.CreateJsonValueConverter<List<string>>());
6164
modelBuilder.Entity<Geofence>()
6265
.Property(p => p.Type)
6366
.HasConversion(x => Geofence.GeofenceTypeToString(x), x => Geofence.StringToGeofenceType(x));

src/Chuck.Data/Entities/Instance.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
namespace Chuck.Data.Entities
22
{
3+
using System;
4+
using System.Collections.Generic;
35
using System.ComponentModel.DataAnnotations;
46
using System.ComponentModel.DataAnnotations.Schema;
57
using System.Text.Json.Serialization;
@@ -35,10 +37,10 @@ public class Instance : BaseEntity, IAggregateRoot
3537
public ushort MaximumLevel { get; set; }
3638

3739
[
38-
Column("geofence"),
39-
JsonPropertyName("geofence"),
40+
Column("geofences"),
41+
JsonPropertyName("geofences"),
4042
]
41-
public string Geofence { get; set; }
43+
public List<string> Geofences { get; set; }
4244

4345
[
4446
Column("data"),

src/ChuckDeviceController/Controllers/ApiController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public async Task<dynamic> GetInstances()
159159
level = $"{instance.MinimumLevel}-{instance.MaximumLevel}",
160160
//count = totalCount == 0 ? "0" : $"{totalCount} ({onlineCount}/{offlineCount})",
161161
count = totalCount == 0 ? "0" : $"{onlineCount}/{offlineCount}|{totalCount}",
162-
geofence = instance.Geofence,
162+
geofences = string.Join(", ", instance.Geofences),
163163
status = await InstanceController.Instance.GetInstanceStatus(instance).ConfigureAwait(false),
164164
buttons = $"<a href='/dashboard/instance/edit/{Uri.EscapeDataString(instance.Name)}' role='button' class='btn btn-sm btn-primary'>Edit Instance</a>",
165165
});

src/ChuckDeviceController/Controllers/DashboardController.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ public async Task<IActionResult> AddInstance()
420420
{
421421
var name = Request.Form["name"].ToString();
422422
var type = Instance.StringToInstanceType(Request.Form["type"]);
423-
var geofence = Request.Form["geofence"].ToString();
423+
var geofences = Request.Form["geofences"].ToString()?.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries)?.ToList();
424424
var area = Request.Form["area"].ToString();
425425
var minLevel = ushort.Parse(Request.Form["min_level"]);
426426
var maxLevel = ushort.Parse(Request.Form["max_level"]);
@@ -462,6 +462,8 @@ public async Task<IActionResult> AddInstance()
462462
}
463463
}
464464

465+
// TODO: Valid geofence names
466+
465467
if (minLevel > maxLevel || minLevel < 0 || minLevel > 40 || maxLevel < 0 || maxLevel > 40)
466468
{
467469
// Invalid levels
@@ -481,7 +483,7 @@ public async Task<IActionResult> AddInstance()
481483
Type = type,
482484
MinimumLevel = minLevel,
483485
MaximumLevel = maxLevel,
484-
Geofence = geofence,
486+
Geofences = geofences,
485487
Data = new InstanceData
486488
{
487489
IVQueueLimit = ivQueueLimit,
@@ -518,7 +520,6 @@ public async Task<IActionResult> EditInstance(string name)
518520
}
519521
dynamic obj = BuildDefaultData();
520522
obj.name = name;
521-
obj.geofence = instance.Geofence;
522523
obj.old_name = name;
523524
obj.min_level = instance.MinimumLevel;
524525
obj.max_level = instance.MaximumLevel;
@@ -543,9 +544,8 @@ public async Task<IActionResult> EditInstance(string name)
543544
{
544545
name = x.Name,
545546
type = x.Type.ToString().ToLower(),
546-
selected = string.Compare(instance.Geofence, x.Name, true) == 0,
547+
selected = instance.Geofences.Contains(x.Name),
547548
});
548-
var geofence = geofences.FirstOrDefault(x => string.Compare(x.Name, instance.Geofence, true) == 0);
549549
obj.circle_route_type = CircleRouteTypeToString(instance.Data.CircleRouteType);
550550
obj.leapfrog_selected = instance.Data.CircleRouteType == CircleRouteType.Default;
551551
obj.spread_selected = instance.Data.CircleRouteType == CircleRouteType.Split;
@@ -609,7 +609,7 @@ public async Task<IActionResult> EditInstance(string name)
609609

610610
var newName = Request.Form["name"].ToString();
611611
var type = Instance.StringToInstanceType(Request.Form["type"]);
612-
var geofence = Request.Form["geofence"].ToString();
612+
var geofences = Request.Form["geofences"].ToString()?.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries)?.ToList();
613613
//var area = Request.Form["area"].ToString();
614614
var minLevel = ushort.Parse(Request.Form["min_level"]);
615615
var maxLevel = ushort.Parse(Request.Form["max_level"]);
@@ -647,7 +647,7 @@ public async Task<IActionResult> EditInstance(string name)
647647
instance.Type = type;
648648
instance.MinimumLevel = minLevel;
649649
instance.MaximumLevel = maxLevel;
650-
instance.Geofence = geofence;
650+
instance.Geofences = geofences;
651651
instance.Data = new InstanceData
652652
{
653653
IVQueueLimit = ivQueueLimit,

src/ChuckDeviceController/JobControllers/GeofenceController.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text.Json;
57
using System.Threading.Tasks;
68

79
using Microsoft.Extensions.Logging;
@@ -66,5 +68,10 @@ public Geofence GetGeofence(string name)
6668
}
6769
return _geofences[name];
6870
}
71+
72+
public List<Geofence> GetGeofences(List<string> names)
73+
{
74+
return names.Select(x => GetGeofence(x)).ToList();
75+
}
6976
}
7077
}

src/ChuckDeviceController/JobControllers/InstanceController.cs

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,11 @@ public async Task<string> GetInstanceStatus(Instance instance)
128128
public async Task AddInstance(Instance instance)
129129
{
130130
IJobController instanceController = null;
131-
var geofence = GeofenceController.Instance.GetGeofence(instance.Geofence);
132-
if (geofence == null)
131+
var geofences = GeofenceController.Instance.GetGeofences(instance.Geofences);
132+
if (geofences == null)
133133
{
134134
// Failed to get geofence, skip?
135-
_logger.LogError($"[{instance.Name}] Failed to get geofence for instance, make sure it is assign one");
135+
_logger.LogError($"[{instance.Name}] Failed to get geofences for instance, make sure it is assign at least one");
136136
return;
137137
}
138138
switch (instance.Type)
@@ -142,22 +142,27 @@ public async Task AddInstance(Instance instance)
142142
case InstanceType.SmartCircleRaid:
143143
try
144144
{
145-
var area = geofence?.Data?.Area;
146-
var coordsArray = (List<Coordinate>)
147-
(
148-
area is List<Coordinate>
149-
? area
150-
: JsonSerializer.Deserialize<List<Coordinate>>(Convert.ToString(area))
151-
);
145+
var coords = new List<Coordinate>();
146+
foreach (var geofence in geofences)
147+
{
148+
var area = geofence?.Data?.Area;
149+
var coordsArray = (List<Coordinate>)
150+
(
151+
area is List<Coordinate>
152+
? area
153+
: JsonSerializer.Deserialize<List<Coordinate>>(Convert.ToString(area))
154+
);
155+
coords.AddRange(coordsArray);
156+
}
152157
var minLevel = instance.MinimumLevel;
153158
var maxLevel = instance.MaximumLevel;
154159
switch (instance.Type)
155160
{
156161
case InstanceType.CirclePokemon:
157-
instanceController = new CircleInstanceController(instance.Name, coordsArray, CircleType.Pokemon, instance.Data.CircleRouteType, minLevel, maxLevel, instance.Data.AccountGroup, instance.Data.IsEvent);
162+
instanceController = new CircleInstanceController(instance.Name, coords, CircleType.Pokemon, instance.Data.CircleRouteType, minLevel, maxLevel, instance.Data.AccountGroup, instance.Data.IsEvent);
158163
break;
159164
case InstanceType.CircleRaid:
160-
instanceController = new CircleInstanceController(instance.Name, coordsArray, CircleType.Raid, CircleRouteType.Default, minLevel, maxLevel, instance.Data.AccountGroup, instance.Data.IsEvent);
165+
instanceController = new CircleInstanceController(instance.Name, coords, CircleType.Raid, CircleRouteType.Default, minLevel, maxLevel, instance.Data.AccountGroup, instance.Data.IsEvent);
161166
break;
162167
case InstanceType.SmartCircleRaid:
163168
// TODO: SmartCircleRaidInstanceController
@@ -175,32 +180,40 @@ area is List<Coordinate>
175180
case InstanceType.FindTTH:
176181
try
177182
{
178-
var area = geofence?.Data?.Area;
179-
var coordsArray = (List<List<Coordinate>>)
180-
(
181-
area is List<List<Coordinate>>
182-
? area
183-
: JsonSerializer.Deserialize<List<List<Coordinate>>>(Convert.ToString(area))
184-
);
185-
var areaArrayEmptyInner = new List<MultiPolygon>();
186-
foreach (var coords in coordsArray)
183+
var multiPolygons = new List<MultiPolygon>();
184+
var coordinates = new List<List<Coordinate>>();
185+
foreach (var geofence in geofences)
187186
{
188-
var multiPolygon = new MultiPolygon();
189-
Coordinate first = null;
190-
for (var i = 0; i < coords.Count; i++)
187+
var area = geofence?.Data?.Area;
188+
var coordsArray = (List<List<Coordinate>>)
189+
(
190+
area is List<List<Coordinate>>
191+
? area
192+
: JsonSerializer.Deserialize<List<List<Coordinate>>>(Convert.ToString(area))
193+
);
194+
coordinates.AddRange(coordsArray);
195+
196+
var areaArrayEmptyInner = new List<MultiPolygon>();
197+
foreach (var coords in coordsArray)
191198
{
192-
var coord = coords[i];
193-
if (i == 0)
199+
var multiPolygon = new MultiPolygon();
200+
Coordinate first = null;
201+
for (var i = 0; i < coords.Count; i++)
202+
{
203+
var coord = coords[i];
204+
if (i == 0)
205+
{
206+
first = coord;
207+
}
208+
multiPolygon.Add(new Polygon(coord.Latitude, coord.Longitude));
209+
}
210+
if (first != null)
194211
{
195-
first = coord;
212+
multiPolygon.Add(new Polygon(first.Latitude, first.Longitude));
196213
}
197-
multiPolygon.Add(new Polygon(coord.Latitude, coord.Longitude));
198-
}
199-
if (first != null)
200-
{
201-
multiPolygon.Add(new Polygon(first.Latitude, first.Longitude));
214+
areaArrayEmptyInner.Add(multiPolygon);
202215
}
203-
areaArrayEmptyInner.Add(multiPolygon);
216+
multiPolygons.AddRange(areaArrayEmptyInner);
204217
}
205218
var minLevel = instance.MinimumLevel;
206219
var maxLevel = instance.MaximumLevel;
@@ -222,20 +235,20 @@ area is List<List<Coordinate>>
222235
}
223236
var spinLimit = instance.Data.SpinLimit ?? 3500;
224237
var retryLimit = instance.Data.QuestRetryLimit ?? 5;
225-
instanceController = new AutoInstanceController(instance.Name, areaArrayEmptyInner, AutoType.Quest, timezoneOffset, minLevel, maxLevel, spinLimit, retryLimit, instance.Data.AccountGroup, instance.Data.IsEvent);
238+
instanceController = new AutoInstanceController(instance.Name, multiPolygons, AutoType.Quest, timezoneOffset, minLevel, maxLevel, spinLimit, retryLimit, instance.Data.AccountGroup, instance.Data.IsEvent);
226239
break;
227240
case InstanceType.PokemonIV:
228241
var ivList = IVListController.Instance.GetIVList(instance.Data.IVList)?.PokemonIDs ?? new List<uint>();
229242
var ivQueueLimit = instance.Data.IVQueueLimit ?? 100;
230-
instanceController = new IVInstanceController(instance.Name, areaArrayEmptyInner, ivList, minLevel, maxLevel, ivQueueLimit, instance.Data.AccountGroup, instance.Data.IsEvent);
243+
instanceController = new IVInstanceController(instance.Name, multiPolygons, ivList, minLevel, maxLevel, ivQueueLimit, instance.Data.AccountGroup, instance.Data.IsEvent);
231244
break;
232245
case InstanceType.Bootstrap:
233246
var circleSize = instance.Data.CircleSize ?? 70;
234247
var fastBootstrapMode = instance.Data.FastBootstrapMode;
235-
instanceController = new BootstrapInstanceController(instance.Name, coordsArray, minLevel, maxLevel, circleSize, fastBootstrapMode, instance.Data.AccountGroup, instance.Data.IsEvent);
248+
instanceController = new BootstrapInstanceController(instance.Name, coordinates, minLevel, maxLevel, circleSize, fastBootstrapMode, instance.Data.AccountGroup, instance.Data.IsEvent);
236249
break;
237250
case InstanceType.FindTTH:
238-
instanceController = new SpawnpointFinderInstanceController(instance.Name, coordsArray, minLevel, maxLevel, instance.Data.AccountGroup, instance.Data.IsEvent);
251+
instanceController = new SpawnpointFinderInstanceController(instance.Name, coordinates, minLevel, maxLevel, instance.Data.AccountGroup, instance.Data.IsEvent);
239252
break;
240253
}
241254
}

src/ChuckDeviceController/Views/instance-add.mustache

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@
127127
<input type="number" class="form-control" name="max_level" value="{{max_level}}" step=1 min="0" max="40" required>
128128
</div>
129129
<div class="form-group">
130-
Select Scan Area
131-
<select class="form-control" name="geofence" required>
130+
Select Scan Areas
131+
<select class="form-control" name="geofences" multiple required>
132132
<option value="" {{#nothing_selected}} selected{{/nothing_selected}} disabled hidden>Choose Geofence</option>
133133
{{#each geofences}}
134134
<option value="{{name}}" class="{{type}}" {{#selected}} selected{{/selected}}>{{name}}</option>
@@ -171,6 +171,7 @@
171171
<input type="checkbox" class="form-check-input" name="fast_bootstrap_mode" checked>
172172
<label class="form-check-label" for="fast_bootstrap_mode">Fast Bootstrap Mode (5 vs 10 seconds load time)</label>
173173
</div>
174+
<br>
174175
<input type="hidden" name="_csrf" value="{{csrf}}">
175176
<button type="submit" class="btn btn-primary">Create</button>
176177
</form>

src/ChuckDeviceController/Views/instance-edit.mustache

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@
132132
<input type="number" class="form-control" name="max_level" value="{{max_level}}" step=1 min="0" max="40" required>
133133
</div>
134134
<div class="form-group">
135-
Select Scan Area
136-
<select class="form-control" name="geofence" required>
135+
Select Scan Areas
136+
<select class="form-control" name="geofences" multiple required>
137137
<option value="" {{#nothing_selected}} selected{{/nothing_selected}} disabled hidden>Choose Geofence</option>
138138
{{#each geofences}}
139139
<option value="{{name}}" class="{{type}}" {{#selected}} selected{{/selected}}>{{name}}</option>
@@ -176,6 +176,7 @@
176176
<input type="checkbox" class="form-check-input" name="fast_bootstrap_mode" {{#fast_bootstrap_mode}} checked{{/fast_bootstrap_mode}}>
177177
<label class="form-check-label" for="fast_bootstrap_mode">Fast Bootstrap Mode (5 vs 10 seconds load time)</label>
178178
</div>
179+
<br>
179180
<input type="hidden" name="_csrf" value="{{csrf}}">
180181
<button type="submit" class="btn btn-primary">Update</button>
181182
</form>

src/ChuckDeviceController/Views/instances.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
{ "data": "type" },
3737
{ "data": "level" },
3838
{ "data": "status" },
39-
{ "data": "geofence" },
39+
{ "data": "geofences" },
4040
{ "data": "count" },
4141
{ "data": "buttons" }
4242
],
@@ -71,7 +71,7 @@
7171
<th class="min-desktop">Type</th>
7272
<th class="min-desktop">Level</th>
7373
<th class="all">Status</th>
74-
<th class="min-desktop">Geofence</th>
74+
<th class="min-desktop">Geofences</th>
7575
<th class="min-desktop">Devices</th>
7676
<th class="all" width="5%"></th>
7777
</tr>

0 commit comments

Comments
 (0)