Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
return await GetDetectedDeviceTimestampHelper(detectedDevices);
}

private async Task<ActionResult<String>> GetDetectedDeviceTimestampHelper(List<DetectedDevice> detectedDevices)

Check warning on line 71 in CrowdedBackend/CrowdedBackend/Controllers/DetectedDevicesController.cs

View workflow job for this annotation

GitHub Actions / format-check

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
List<(float x, float y)> listOfDeviceLocations = [];
foreach (var detectedDevice in detectedDevices)
Expand All @@ -93,8 +93,8 @@
raspLocations.Add(((float)rasp.RaspX, (float)rasp.RaspY));
}

String heatmapBase64Encoded = HeatmapGenerator.Generate(venue.VenueName, raspLocations, listOfDeviceLocations);

String heatmapBase64Encoded = HeatmapGenerator.Generate(venue.VenueName, raspLocations, listOfDeviceLocations);
return heatmapBase64Encoded;
}

Expand Down
5 changes: 5 additions & 0 deletions CrowdedBackend/CrowdedBackend/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,17 @@
});


builder.Services.AddSignalR();


builder.Services.AddSignalR();

var app = builder.Build();

app.MapHub<DetectedDeviceHub>("/hubs/detecteddevices");

app.MapHub<DetectedDeviceHub>("/hubs/detecteddevices");

Comment on lines +41 to +49

Copilot AI May 9, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate registration of SignalR services detected. Ensure that SignalR is only added once to avoid potential conflicts or unexpected behavior.

Suggested change
builder.Services.AddSignalR();
var app = builder.Build();
app.MapHub<DetectedDeviceHub>("/hubs/detecteddevices");
app.MapHub<DetectedDeviceHub>("/hubs/detecteddevices");
var app = builder.Build();
app.MapHub<DetectedDeviceHub>("/hubs/detecteddevices");

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +49

Copilot AI May 9, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate hub mapping for '/hubs/detecteddevices' found. Remove the redundant mapping to prevent routing conflicts.

Suggested change
builder.Services.AddSignalR();
var app = builder.Build();
app.MapHub<DetectedDeviceHub>("/hubs/detecteddevices");
app.MapHub<DetectedDeviceHub>("/hubs/detecteddevices");
var app = builder.Build();
app.MapHub<DetectedDeviceHub>("/hubs/detecteddevices");

Copilot uses AI. Check for mistakes.
app.UseHttpLogging();

app.UseHttpsRedirection();
Expand Down
69 changes: 38 additions & 31 deletions crowdedapp/lib/canteen_pages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ class _CanteenPageState extends State<CanteenPage> {
HubConnection? _hubConnection;
// Track when the image was last updated
DateTime? _lastUpdated;
final GlobalKey _futureBuilderKey = GlobalKey();
bool _needsRefresh = false;
Image? _lastImage;

@override
void initState() {
super.initState();
_needsRefresh = true; // Ensure image loads on first open
_initSignalR();
}

Expand All @@ -36,8 +37,6 @@ class _CanteenPageState extends State<CanteenPage> {
'$backendUrl/hubs/detecteddevices',
HttpConnectionOptions(
logging: (level, message) => print('SignalR $level: $message'),
skipNegotiation: true,
transport: HttpTransportType.webSockets,
),
)
.withAutomaticReconnect()
Expand All @@ -57,13 +56,16 @@ class _CanteenPageState extends State<CanteenPage> {
}
});
});
_hubConnection?.on('NewDevicesDetected', (arguments) {
print('Received NewDevicesDetected: $arguments');
_hubConnection?.on('NewDevicesDetected', (arguments) async {
print('SignalR: NewDevicesDetected event received!');
print('Jeg bliver ikke mounted');

Copilot AI May 9, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Debug print statements such as this should be removed or replaced with proper logging before merging to production to reduce noise.

Suggested change
print('Jeg bliver ikke mounted');
log('SignalR: Component not mounted during NewDevicesDetected event.', name: 'CanteenPage');

Copilot uses AI. Check for mistakes.
if (mounted) {
print('Jeg skal opdatere heatmap');
setState(() {
_needsRefresh = true;
_lastUpdated = DateTime.now();
});
// Optionally, immediately trigger a fetch so the UI updates as soon as possible
await _fetchLatestHeatmapImage();
}
});
}
Expand All @@ -89,32 +91,35 @@ class _CanteenPageState extends State<CanteenPage> {

// Fetch the latest valid heatmap image from the new endpoint
Future<Image?> _fetchLatestHeatmapImage() async {
// Add a short delay to ensure backend has generated the new heatmap
await Future.delayed(const Duration(milliseconds: 500));
try {
final response = await http.get(
Uri.parse('$backendUrl/api/DetectedDevices/getLatestValidHeatmap'),
);
if (response.statusCode == 200) {
final bytes = base64Decode(response.body);
// Update the last updated time
if (bytes.isEmpty) {
print('Error: Received empty image data');
return null;
}
final image = Image.memory(bytes, fit: BoxFit.contain);
if (mounted) {
print('Jeg kommer her ind');
setState(() {
_lastImage = image;
_lastUpdated = DateTime.now();
_needsRefresh = false;
});
}
return Image.memory(bytes, fit: BoxFit.contain);
return image;
} else {
print('Error fetching heatmap: Status \\${response.statusCode}');
return null;
}
} catch (e) {
print('Error fetching heatmap: $e');
return null;
} finally {
if (_needsRefresh) {
setState(() {
_needsRefresh = false;
});
}
}
}

Expand Down Expand Up @@ -152,7 +157,7 @@ class _CanteenPageState extends State<CanteenPage> {
),
SizedBox(height: 10),
Text(
"Here you are able to view the heatmao of the canteen. The Data in updated every 10 seconds.",
"Here you are able to view the heatmap of the canteen. The Data is updated every 10 seconds.",
style: TextStyle(fontSize: 16, color: Colors.white70),
),
SizedBox(height: 150),
Expand All @@ -174,22 +179,24 @@ class _CanteenPageState extends State<CanteenPage> {
child: Container(
width: 500,
height: 500,
color: Colors.grey[300],
child: FutureBuilder<Image?>(
key: _needsRefresh ? UniqueKey() : _futureBuilderKey,
future: _fetchLatestHeatmapImage(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error loading heatmap: \\${snapshot.error}', style: TextStyle(color: Colors.red)));
} else if (snapshot.hasData && snapshot.data != null) {
return snapshot.data!;
} else {
return Icon(Icons.error_outline, size: 200, color: Colors.red);
}
},
),
color: Colors.transparent,
child: _needsRefresh
? FutureBuilder<Image?>(
key: UniqueKey(),
future: _fetchLatestHeatmapImage(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error loading heatmap: \\${snapshot.error}', style: TextStyle(color: Colors.red)));
} else if (snapshot.hasData && snapshot.data != null) {
return snapshot.data!;
} else {
return Icon(Icons.error_outline, size: 200, color: Colors.red);
}
},
)
: (_lastImage ?? Icon(Icons.restaurant, size: 200, color: Colors.black54)),
),
),
),
Expand Down
4 changes: 2 additions & 2 deletions crowdedapp/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,10 @@ packages:
dependency: "direct main"
description:
name: http
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "1.4.0"
http_parser:
dependency: transitive
description:
Expand Down
12 changes: 0 additions & 12 deletions crowdedapp/test/canteen_loading_test.dart

This file was deleted.

16 changes: 16 additions & 0 deletions crowdedapp/test/canteen_page_render_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:crowdedapp/canteen_pages.dart';
import 'package:flutter/material.dart';

void main() {
testWidgets('CanteenPage renders without crashing', (WidgetTester tester) async {
await tester.runAsync(() async {
await tester.pumpWidget(const MaterialApp(home: CanteenPage(title: 'Canteen View')));
// Wait for any pending timers to complete (simulate enough time for all timers)
await Future.delayed(const Duration(seconds: 6));
await tester.pumpAndSettle();
// Just check that the widget tree contains CanteenPage
expect(find.byType(CanteenPage), findsOneWidget);
});
});
}
18 changes: 10 additions & 8 deletions crowdedapp/test/navigation_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ import 'package:flutter/material.dart';

void main() {
testWidgets('Bottom navigation switches pages', (WidgetTester tester) async {
await tester.pumpWidget(const Crowdedapp());
await tester.runAsync(() async {
await tester.pumpWidget(const Crowdedapp());

// Home page should be visible
expect(find.text('Welcome to Horizon'), findsOneWidget);
// Home page should be visible
expect(find.text('Welcome to Horizon'), findsOneWidget);

// Tap the Canteen tab
await tester.tap(find.byIcon(Icons.restaurant));
await tester.pumpAndSettle();
// Tap the Canteen tab
await tester.tap(find.byIcon(Icons.restaurant));
await tester.pump(const Duration(seconds: 1)); // Use a fixed pump duration instead of pumpAndSettle

Copilot AI May 9, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a fixed pump duration might lead to flaky tests if UI animations or state transitions take longer than expected. Consider using pumpAndSettle or a more dynamic wait mechanism to ensure reliable test behavior.

Suggested change
await tester.pump(const Duration(seconds: 1)); // Use a fixed pump duration instead of pumpAndSettle
await tester.pumpAndSettle(); // Dynamically wait for all animations and transitions to complete

Copilot uses AI. Check for mistakes.

// Canteen page should be visible
expect(find.text('Canteen View'), findsOneWidget);
// Canteen page should be visible
expect(find.text('Canteen View'), findsOneWidget);
});
});
}
Loading