Integrated realtime data.

This commit is contained in:
Faris Armoush
2025-05-05 16:58:48 +03:00
parent 4d5de7bc05
commit 38184ca8b2
8 changed files with 70 additions and 18 deletions

View File

@ -6,10 +6,12 @@ import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_by_phases/energy_consumption_by_phases_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_by_phases/energy_consumption_by_phases_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_per_device/energy_consumption_per_device_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_per_device/energy_consumption_per_device_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/total_energy_consumption/total_energy_consumption_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/total_energy_consumption/total_energy_consumption_bloc.dart';
import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/fake_energy_consumption_by_phases_service.dart'; import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/fake_energy_consumption_by_phases_service.dart';
import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/fake_energy_consumption_per_device_service.dart'; import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/fake_energy_consumption_per_device_service.dart';
import 'package:syncrow_web/pages/analytics/services/power_clamp_info/remote_power_clamp_info_service.dart'; import 'package:syncrow_web/pages/analytics/services/power_clamp_info/remote_power_clamp_info_service.dart';
import 'package:syncrow_web/pages/analytics/services/realtime_device_service/firebase_realtime_device_service.dart';
import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/fake_total_energy_consumption_service.dart'; import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/fake_total_energy_consumption_service.dart';
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
@ -46,6 +48,11 @@ class AnalyticsPage extends StatelessWidget {
RemotePowerClampInfoService(HTTPService()), RemotePowerClampInfoService(HTTPService()),
), ),
), ),
BlocProvider(
create: (context) => RealtimeDeviceChangesBloc(
FirebaseRealtimeDeviceService(),
),
),
], ],
child: const AnalyticsPageForm(), child: const AnalyticsPageForm(),
); );

View File

@ -43,5 +43,13 @@ class PowerClampInfoBloc extends Bloc<PowerClampInfoEvent, PowerClampInfoState>
void _onUpdatePowerClampStatusEvent( void _onUpdatePowerClampStatusEvent(
UpdatePowerClampStatusEvent event, UpdatePowerClampStatusEvent event,
Emitter<PowerClampInfoState> emit, Emitter<PowerClampInfoState> emit,
) async {} ) async {
final currentModel = state.powerClampModel;
if (currentModel == null) return;
final updatedStatus = PowerStatus.fromStatusList(event.statusList);
final updatedModel = currentModel.copyWith(statusPower: updatedStatus);
emit(state.copyWith(powerClampModel: updatedModel));
}
} }

View File

@ -25,8 +25,12 @@ class RealtimeDeviceChangesBloc
onData: (data) { onData: (data) {
final currentState = state; final currentState = state;
emit( emit(
state.copyWith( state.copyWith(
deviceStatusList: [...currentState.deviceStatusList, data], deviceStatusList: [
...currentState.deviceStatusList.where((device) =>
!data.any((newDevice) => newDevice.code == device.code)),
...data,
],
), ),
); );
}, },

View File

@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_by_phases/energy_consumption_by_phases_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_by_phases/energy_consumption_by_phases_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_per_device/energy_consumption_per_device_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_per_device/energy_consumption_per_device_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/total_energy_consumption/total_energy_consumption_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/total_energy_consumption/total_energy_consumption_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart_box.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart_box.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_widget.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/power_clamp_energy_data_widget.dart';
@ -28,6 +29,12 @@ class _AnalyticsEnergyManagementViewState
_loadTotalEnergyConsumption(); _loadTotalEnergyConsumption();
_loadEnergyConsumptionPerDevice(); _loadEnergyConsumptionPerDevice();
_loadPowerClampInfo(); _loadPowerClampInfo();
context.read<RealtimeDeviceChangesBloc>().add(
const RealtimeDeviceChangesStarted(
'cb71d6ad-6e29-4eaa-ae3e-1a0d1c5f60fa',
),
);
super.initState(); super.initState();
} }

View File

@ -19,9 +19,11 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocListener<RealtimeDeviceChangesBloc, RealtimeDeviceChangesState>( return BlocListener<RealtimeDeviceChangesBloc, RealtimeDeviceChangesState>(
listener: (context, state) { listener: (context, state) {
context.read<PowerClampInfoBloc>().add( if (state.status != RealtimeDeviceChangesStatus.loaded) {
UpdatePowerClampStatusEvent(state.deviceStatusList), context.read<PowerClampInfoBloc>().add(
); UpdatePowerClampStatusEvent(state.deviceStatusList),
);
}
}, },
child: BlocBuilder<PowerClampInfoBloc, PowerClampInfoState>( child: BlocBuilder<PowerClampInfoBloc, PowerClampInfoState>(
builder: (context, state) { builder: (context, state) {

View File

@ -4,7 +4,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_sta
class FirebaseRealtimeDeviceService implements RealtimeDeviceService { class FirebaseRealtimeDeviceService implements RealtimeDeviceService {
@override @override
Stream<Status> subscribe(String deviceId) { Stream<List<Status>> subscribe(String deviceId) {
try { try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); final ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
@ -15,11 +15,17 @@ class FirebaseRealtimeDeviceService implements RealtimeDeviceService {
throw Exception('Invalid data received from Firebase'); throw Exception('Invalid data received from Firebase');
} }
final statusMap = data['status'] as Map<dynamic, dynamic>; final statusMap = data['status'] as List<dynamic>;
return Status( return statusMap.map((dynamic status) {
code: statusMap['code'] as String? ?? '', if (status is! Map<dynamic, dynamic>) {
value: statusMap['value'] as String? ?? '', throw Exception('Invalid status format');
); }
return Status(
code: status['code']?.toString() ?? '',
value: num.tryParse(status['value']?.toString() ?? '0') ,
);
}).toList();
}); });
} catch (e) { } catch (e) {
throw Exception('Error subscribing to device status: $e'); throw Exception('Error subscribing to device status: $e');

View File

@ -1,5 +1,5 @@
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
abstract interface class RealtimeDeviceService { abstract interface class RealtimeDeviceService {
Stream<Status> subscribe(String deviceId); Stream<List<Status>> subscribe(String deviceId);
} }

View File

@ -1,4 +1,6 @@
// PowerClampModel class to represent the response // PowerClampModel class to represent the response
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
class PowerClampModel { class PowerClampModel {
String productUuid; String productUuid;
String productType; String productType;
@ -46,11 +48,27 @@ class PowerStatus {
factory PowerStatus.fromJson(Map<String, dynamic> json) { factory PowerStatus.fromJson(Map<String, dynamic> json) {
return PowerStatus( return PowerStatus(
phaseA: Phase.fromJson(json['phaseA']as List<dynamic>? ?? []), phaseA: Phase.fromJson(json['phaseA'] as List<dynamic>? ?? []),
phaseB: Phase.fromJson(json['phaseB']as List<dynamic>? ?? []), phaseB: Phase.fromJson(json['phaseB'] as List<dynamic>? ?? []),
phaseC: Phase.fromJson(json['phaseC']as List<dynamic>? ?? []), phaseC: Phase.fromJson(json['phaseC'] as List<dynamic>? ?? []),
general: Phase.fromJson(json['general']as List<dynamic>? ?? [] general: Phase.fromJson(json['general'] as List<dynamic>? ?? []),
)); );
}
factory PowerStatus.fromStatusList(List<Status> statuses) {
List<DataPoint> extractPhase(String prefix) {
return statuses
.where((s) => s.code.endsWith(prefix))
.map((s) => DataPoint(code: s.code, value: s.value))
.toList();
}
return PowerStatus(
phaseA: Phase(dataPoints: extractPhase('A')),
phaseB: Phase(dataPoints: extractPhase('B')),
phaseC: Phase(dataPoints: extractPhase('C')),
general: Phase(dataPoints: extractPhase('')),
);
} }
} }