Added chatterbox connections
This commit is contained in:
1 parent
41ef12f954
commit
e58ceeb192
58 files changed
+8931
-8
No files matched your search
@@ -0,0 +1,126 @@
|
||||
import 'room_page.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
void main() => runApp(const GlitchLab());
|
||||
class GlitchLab extends StatelessWidget {
|
||||
const GlitchLab({super.key});
|
||||
@override
|
||||
Widget build(BuildContext context) => MaterialApp(
|
||||
title: 'Glitch GM Lab',
|
||||
theme: ThemeData.dark(useMaterial3: true).copyWith(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.cyan, brightness: Brightness.dark)),
|
||||
home: Uri.base.queryParameters['room'] == '1' ? const RoomPage() : const CabinetPage(),
|
||||
);
|
||||
}
|
||||
class CabinetPage extends StatefulWidget {
|
||||
const CabinetPage({super.key, this.client});
|
||||
final http.Client? client;
|
||||
@override
|
||||
State<CabinetPage> createState() => _CabinetPageState();
|
||||
}
|
||||
class _CabinetPageState extends State<CabinetPage> {
|
||||
late final http.Client client;
|
||||
Timer? timer;
|
||||
Map<String, dynamic>? cabinet;
|
||||
String? error;
|
||||
String notice = 'Connecting to the local simulation...';
|
||||
bool pending = false;
|
||||
bool polling = false;
|
||||
int sequence = 0;
|
||||
int revision = 0;
|
||||
static const base = 'http://127.0.0.1:18766';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
client = widget.client ?? http.Client();
|
||||
refresh();
|
||||
timer = Timer.periodic(const Duration(seconds: 1), (_) => refresh());
|
||||
}
|
||||
Future<void> refresh() async {
|
||||
if (polling || pending) return;
|
||||
polling = true;
|
||||
final requestRevision = revision;
|
||||
try {
|
||||
final response = await client.get(Uri.parse('$base/state')).timeout(const Duration(seconds: 3));
|
||||
if (response.statusCode != 200) throw Exception('Cabinet or controller unavailable');
|
||||
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
if (mounted && !pending && requestRevision == revision) setState(() { cabinet = data['state']; error = null; });
|
||||
} catch (_) {
|
||||
if (mounted && requestRevision == revision) setState(() => error = 'Disconnected - controls disabled. Check the lab terminals.');
|
||||
} finally { polling = false; }
|
||||
}
|
||||
Future<void> command(String action) async {
|
||||
if (pending || error != null || cabinet == null) return;
|
||||
revision++;
|
||||
setState(() { pending = true; notice = 'Waiting for cabinet acknowledgement...'; });
|
||||
try {
|
||||
final response = await client.post(Uri.parse('$base/command'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode({
|
||||
'id': 'gm-${DateTime.now().microsecondsSinceEpoch}-${sequence++}',
|
||||
'session': cabinet!['session'],
|
||||
'expires_ms': DateTime.now().millisecondsSinceEpoch + 10000,
|
||||
'action': action,
|
||||
})).timeout(const Duration(seconds: 4));
|
||||
if (response.statusCode != 200) throw Exception('No acknowledgement');
|
||||
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
if (mounted) setState(() {
|
||||
cabinet = data['state'];
|
||||
notice = '${data['accepted'] == true ? 'Accepted' : 'Rejected'}: ${data['message']}';
|
||||
error = null;
|
||||
});
|
||||
} catch (_) {
|
||||
if (mounted) setState(() {
|
||||
error = 'Delivery unconfirmed. Refresh state before trying again.';
|
||||
notice = 'No success acknowledgement received.';
|
||||
});
|
||||
} finally { if (mounted) setState(() => pending = false); }
|
||||
}
|
||||
@override
|
||||
void dispose() { timer?.cancel(); client.close(); super.dispose(); }
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final phase = cabinet?['phase'] ?? 'unknown';
|
||||
final available = cabinet != null && error == null && !pending;
|
||||
Widget control(String label, String action, bool enabled) => FilledButton(
|
||||
onPressed: available && enabled ? () => command(action) : null, child: Text(label));
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Escape Gallery / Glitch: the Dark Machine')),
|
||||
body: Center(child: ConstrainedBox(constraints: const BoxConstraints(maxWidth: 900),
|
||||
child: ListView(padding: const EdgeInsets.all(24), children: [
|
||||
Text(cabinet?['simulated'] == false ? 'INTEGRATION LAB - REAL GODOT CABINET' : 'INTEGRATION LAB - SIMULATED CABINET', style: const TextStyle(color: Colors.amber)),
|
||||
const SizedBox(height: 16),
|
||||
const Text('Cabinet 1 / Pac-Man', style: TextStyle(fontSize: 28)),
|
||||
Text(cabinet?['simulated'] == false ? 'Real Pac-Man. Target: 500 points. No reward outputs. RFID injection: ${cabinet?['rfid_injection'] == true ? 'enabled' : 'disabled'}.' : 'No physical RFID, LEDs, GPIO, or arcade game is connected.'),
|
||||
const SizedBox(height: 24),
|
||||
Card(child: Padding(padding: const EdgeInsets.all(24), child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Text(phase.replaceAll('_', ' ').toUpperCase(), style: const TextStyle(fontSize: 32)),
|
||||
Text('Credits: ${cabinet?['credits'] ?? '-'} Accepted scans: ${cabinet?['scans'] ?? '-'}'),
|
||||
const SizedBox(height: 12),
|
||||
Text(phase == 'powered_off' ? 'Waiting for operator' :
|
||||
phase == 'booting' ? 'Four-second boot sequence...' :
|
||||
phase == 'ready' ? 'Scan a card, then press Start' :
|
||||
phase == 'playing' ? 'Game in progress' :
|
||||
phase == 'won' ? 'Objective completed' : phase == 'lost' ? 'Game lost - reset the test to retry' : 'Awaiting state'),
|
||||
]))),
|
||||
const SizedBox(height: 16),
|
||||
Wrap(spacing: 12, runSpacing: 12, children: [
|
||||
control('Power on', 'power_on', phase == 'powered_off'),
|
||||
control('Simulate RFID scan', 'scan', phase == 'ready' && (cabinet?['simulated'] == true || cabinet?['rfid_injection'] == true)),
|
||||
control('Start game', 'start', phase == 'ready' && cabinet?['credits'] == 1),
|
||||
control('Simulate win', 'win', phase == 'playing' && cabinet?['simulated'] == true),
|
||||
control('Reset test', 'reset', true),
|
||||
]),
|
||||
const SizedBox(height: 24),
|
||||
Text(error ?? notice, style: TextStyle(color: error == null ? Colors.white70 : Colors.orange)),
|
||||
const SizedBox(height: 16),
|
||||
const Text('Flutter → local JSON gateway → NATS / MessagePack → simulated cabinet'),
|
||||
]))),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user