pushed after improved logging

appointment_merge
Faiz Hashmi 3 months ago
parent 90d1a9329a
commit 0779955ef0

@ -3,9 +3,9 @@ import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:hmg_qline/utilities/api_exception.dart';
typedef FactoryConstructor<U> = U Function(dynamic);
@ -14,14 +14,18 @@ abstract class ApiClient {
Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
Future<Response> postJsonForResponse<T>(String url, T jsonObject, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
Future<http.Response> postJsonForResponse<T>(String url, T jsonObject, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
Future<U> getJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
Future<Response> getJsonForResponse<T>(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
Future<http.Response> getJsonForResponse<T>(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0});
}
class ApiClientImp implements ApiClient {
LoggerService loggerService;
ApiClientImp({required this.loggerService});
@override
Future<U> postJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url, T jsonObject,
{String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
@ -45,7 +49,7 @@ class ApiClientImp implements ApiClient {
}
@override
Future<Response> postJsonForResponse<T>(String url, T jsonObject, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
Future<http.Response> postJsonForResponse<T>(String url, T jsonObject, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
String? requestBody;
if (jsonObject != null) {
requestBody = jsonEncode(jsonObject);
@ -59,7 +63,7 @@ class ApiClientImp implements ApiClient {
return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes);
}
Future<Response> _postForResponse(
Future<http.Response> _postForResponse(
String url,
requestBody, {
String? token,
@ -81,17 +85,17 @@ class ApiClientImp implements ApiClient {
var queryString = Uri(queryParameters: queryParameters).query;
url = '$url?$queryString';
}
Response response;
http.Response response;
response = await _post(Uri.parse(url), body: requestBody, headers: headers0).timeout(const Duration(seconds: 100));
if (!kReleaseMode) {
logger.d("------URL------");
logger.i(url);
logger.d("------Payload------");
logger.i(jsonDecode(requestBody));
logger.d("------Response------");
logger.i(jsonDecode(response.body));
loggerService.logInfo("------URL------");
loggerService.logInfo(url);
loggerService.logInfo("------Payload------");
loggerService.logInfo(jsonDecode(requestBody).toString());
loggerService.logInfo("------Response------");
loggerService.logInfo(jsonDecode(response.body).toString());
}
if (response.statusCode >= 200 && response.statusCode < 500) {
var jsonData = jsonDecode(response.body);
@ -120,7 +124,7 @@ class ApiClientImp implements ApiClient {
}
} on TimeoutException catch (e) {
throw APIException(APIException.timeout, arguments: e);
} on ClientException catch (e) {
} on http.ClientException catch (e) {
if (retryTimes > 0) {
log('will retry after 3 seconds...');
await Future.delayed(const Duration(seconds: 3));
@ -136,7 +140,7 @@ class ApiClientImp implements ApiClient {
bool _certificateCheck(X509Certificate cert, String host, int port) => true;
Future<T> _withClient<T>(Future<T> Function(Client) fn) async {
Future<T> _withClient<T>(Future<T> Function(http.Client) fn) async {
var httpClient = HttpClient()..badCertificateCallback = _certificateCheck;
var client = IOClient(httpClient);
try {
@ -146,7 +150,7 @@ class ApiClientImp implements ApiClient {
}
}
Future<Response> _post(url, {Map<String, String>? headers, body, Encoding? encoding}) => _withClient((client) => client.post(url, headers: headers, body: body, encoding: encoding));
Future<http.Response> _post(url, {Map<String, String>? headers, body, Encoding? encoding}) => _withClient((client) => client.post(url, headers: headers, body: body, encoding: encoding));
@override
Future<U> getJsonForObject<T, U>(FactoryConstructor<U> factoryConstructor, String url,
@ -166,7 +170,7 @@ class ApiClientImp implements ApiClient {
}
@override
Future<Response> getJsonForResponse<T>(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
Future<http.Response> getJsonForResponse<T>(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
if (headers == null) {
headers = {'Content-Type': 'application/json'};
} else {
@ -177,7 +181,7 @@ class ApiClientImp implements ApiClient {
bool isFirstCall = true;
Future<Response> _getForResponse(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
Future<http.Response> _getForResponse(String url, {String? token, Map<String, dynamic>? queryParameters, Map<String, String>? headers, int retryTimes = 0}) async {
try {
var headers0 = <String, String>{};
if (token != null) {
@ -193,13 +197,13 @@ class ApiClientImp implements ApiClient {
}
if (!kReleaseMode) {
logger.i(url);
logger.i("$queryParameters");
loggerService.logInfo(url);
loggerService.logInfo("$queryParameters");
}
var response = await _get(Uri.parse(url), headers: headers0).timeout(const Duration(seconds: 60));
if (!kReleaseMode) {
logger.i(jsonDecode(response.body));
loggerService.logInfo(jsonDecode(response.body));
}
if (response.statusCode >= 200 && response.statusCode < 500) {
var jsonData = jsonDecode(response.body);
@ -229,7 +233,7 @@ class ApiClientImp implements ApiClient {
}
} on TimeoutException catch (e) {
throw APIException(APIException.timeout, arguments: e);
} on ClientException catch (e) {
} on http.ClientException catch (e) {
if (retryTimes > 0) {
await Future.delayed(const Duration(seconds: 3));
return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1);
@ -239,5 +243,5 @@ class ApiClientImp implements ApiClient {
}
}
Future<Response> _get(url, {Map<String, String>? headers}) => _withClient((client) => client.get(url, headers: headers));
Future<http.Response> _get(url, {Map<String, String>? headers}) => _withClient((client) => client.get(url, headers: headers));
}

@ -10,28 +10,41 @@ import 'package:hmg_qline/repositories/signalR_repo.dart';
import 'package:hmg_qline/services/audio_service.dart';
import 'package:hmg_qline/services/cache_service.dart';
import 'package:hmg_qline/services/connectivity_service.dart';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:hmg_qline/services/text_to_speech_service.dart';
import 'package:hmg_qline/view_models/queuing_view_model.dart';
import 'package:hmg_qline/view_models/screen_config_view_model.dart';
import 'package:just_audio/just_audio.dart';
import 'package:logger/logger.dart';
import 'package:shared_preferences/shared_preferences.dart';
final getIt = GetIt.instance;
class AppDependencies {
static Future<void> addDependencies() async {
final logger = Logger(
printer: PrettyPrinter(
printEmojis: false,
colors: true,
dateTimeFormat: DateTimeFormat.none,
),
);
//api client
getIt.registerSingleton<ApiClient>(ApiClientImp());
getIt.registerSingleton<LoggerService>(LoggerServiceImp(logger: logger));
getIt.registerSingleton<ApiClient>(ApiClientImp(loggerService: getIt.get<LoggerService>()));
//repos
getIt.registerSingleton<SignalrRepo>(SignalrRepoImp());
getIt.registerSingleton<ScreenDetailsRepo>(ScreenDetailsRepoImp(apiClientInstance: getIt.get<ApiClient>()));
getIt.registerSingleton<SignalrRepo>(SignalrRepoImp(loggerService: getIt.get<LoggerService>()));
getIt.registerSingleton<ScreenDetailsRepo>(ScreenDetailsRepoImp(apiClientInstance: getIt.get<ApiClient>(), loggerService: getIt.get<LoggerService>()));
//ThirdPartyServices
getIt.registerSingleton<ConnectivityService>(ConnectivityServiceImp(connectivityInstance: Connectivity()));
getIt.registerSingleton<CacheService>(CacheServiceImp(preferencesInstance: await SharedPreferences.getInstance()));
getIt.registerSingleton<AudioService>(AudioServiceImp(audioPlayerInstance: AudioPlayer()));
getIt.registerSingleton<TextToSpeechService>(TextToSpeechServiceImp(textToSpeechInstance: FlutterTts()));
getIt.registerSingleton<TextToSpeechService>(TextToSpeechServiceImp(textToSpeechInstance: FlutterTts(), loggerService: getIt.get<LoggerService>()));
//ViewModels
@ -40,11 +53,13 @@ class AppDependencies {
screenDetailsRepo: getIt.get<ScreenDetailsRepo>(),
cacheService: getIt.get<CacheService>(),
connectivityService: getIt.get<ConnectivityService>(),
loggerService: getIt.get<LoggerService>(),
),
);
getIt.registerSingleton<QueuingViewModel>(
QueuingViewModel(
loggerService: getIt.get<LoggerService>(),
screenDetailsRepo: getIt.get<ScreenDetailsRepo>(),
cacheService: getIt.get<CacheService>(),
textToSpeechService: getIt.get<TextToSpeechService>(),

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
bool useTestIP = false;
Logger logger = Logger(printer: PrettyPrinter(printEmojis: false, colors: true, dateTimeFormat: DateTimeFormat.none));
// app globals
@ -20,7 +19,10 @@ class AppStrings {
static String awaitingQueueNumberAr = "في انتظار رقم قائمة الانتظار";
static String counterNo = "Counter Number: ";
static String awaitingArrivalAr = "في انتظار وصول المرضى";
static String configurationIssueContactAdmin = "There is something wrong with configuration. Please contact Admin.";
static String configurationIssueContactAdmin = "There is something wrong with configuration. Please contact IT Support.";
static String dataLogsFileName = "data_logs.txt";
static String errorLogsFileName = "error_logs.txt";
}
class AppColors {
@ -102,7 +104,7 @@ class AppConstants {
static String testIP = '12.4.5.1'; // projectID.QlineType.ScreenType.AnyNumber (1 to 10)
static int thresholdForListUI = 3;
static int currentBuildVersion = 6;
static int currentBuildVersion = 7;
}
class ApiConstants {

@ -6,6 +6,7 @@ import 'package:hmg_qline/models/kiosk_ticket_model.dart';
import 'package:hmg_qline/models/prayers_widget_model.dart';
import 'package:hmg_qline/models/rss_feed_model.dart';
import 'package:hmg_qline/models/weathers_widget_model.dart';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:hmg_qline/utilities/extensions.dart';
import 'package:hmg_qline/views/view_helpers/info_components.dart';
@ -17,8 +18,6 @@ abstract class ScreenDetailsRepo {
Future<GenericRespModel?> createTicketFromKiosk({required int projectId, required int queueId, int patientId = 0});
// Future<WidgetsConfigModel?> getScreenConfigurationsByIP({required String ipAddress});
Future<WeathersWidgetModel?> getWeatherDetailsByCity({required String cityId});
Future<PrayersWidgetModel?> getPrayerDetailsByLatLong({required double latitude, required double longitude});
@ -32,8 +31,9 @@ abstract class ScreenDetailsRepo {
class ScreenDetailsRepoImp implements ScreenDetailsRepo {
ApiClient apiClientInstance;
LoggerService loggerService;
ScreenDetailsRepoImp({required this.apiClientInstance});
ScreenDetailsRepoImp({required this.apiClientInstance, required this.loggerService});
@override
Future<GlobalConfigurationsModel?> getGlobalScreenConfigurations({required String ipAddress}) async {
@ -49,11 +49,14 @@ class ScreenDetailsRepoImp implements ScreenDetailsRepo {
);
List<GlobalConfigurationsModel> globalConfigurationsModel = List.generate(genericModel.data.length, (index) => GlobalConfigurationsModel.fromJson(json: genericModel.data[index]));
if (globalConfigurationsModel.isNotEmpty) {
loggerService.logToFile(globalConfigurationsModel.toString(), type: LogTypeEnum.data);
return globalConfigurationsModel.first;
}
return null;
} catch (e) {
logger.e(e.toString());
loggerService.logError(e.toString());
loggerService.logToFile(e.toString(), type: LogTypeEnum.error);
InfoComponents.showToast(e.toString());
return null;
}
@ -78,7 +81,8 @@ class ScreenDetailsRepoImp implements ScreenDetailsRepo {
);
return genericModel;
} catch (e) {
logger.e(e.toString());
loggerService.logError(e.toString());
loggerService.logToFile(e.toString(), type: LogTypeEnum.error);
InfoComponents.showToast(e.toString());
return null;
}
@ -104,33 +108,13 @@ class ScreenDetailsRepoImp implements ScreenDetailsRepo {
genericRespModel.data = KioskPatientTicket.fromJson(genericRespModel.data);
return genericRespModel;
} catch (e) {
logger.e(e.toString());
loggerService.logError(e.toString());
loggerService.logToFile(e.toString(), type: LogTypeEnum.error);
InfoComponents.showToast(e.toString());
return null;
}
}
// @override
// Future<WidgetsConfigModel?> getScreenConfigurationsByIP({required String ipAddress}) async {
// try {
// final body = {
// "ipAddress": ipAddress.toString(),
// };
//
// GenericRespModel genericRespModel = await apiClientInstance.postJsonForObject(
// (json) => GenericRespModel.fromJson(json),
// ApiConstants.waitingAreaScreenConfigGet,
// body,
// );
// WidgetsConfigModel widgetsConfigModel = WidgetsConfigModel.fromJson(genericRespModel.data);
// return widgetsConfigModel;
// } catch (e) {
// logger.e(e.toString());
// InfoComponents.showToast(e.toString());
// return null;
// }
// }
@override
Future<WeathersWidgetModel?> getWeatherDetailsByCity({required String cityId}) async {
try {
@ -147,7 +131,8 @@ class ScreenDetailsRepoImp implements ScreenDetailsRepo {
}
return null;
} catch (e) {
logger.e(e.toString());
loggerService.logError(e.toString());
loggerService.logToFile(e.toString(), type: LogTypeEnum.error);
InfoComponents.showToast(e.toString());
return null;
}
@ -170,7 +155,8 @@ class ScreenDetailsRepoImp implements ScreenDetailsRepo {
return null;
} catch (e) {
logger.e(e.toString());
loggerService.logError(e.toString());
loggerService.logToFile(e.toString(), type: LogTypeEnum.error);
InfoComponents.showToast(e.toString());
return null;
}
@ -192,7 +178,8 @@ class ScreenDetailsRepoImp implements ScreenDetailsRepo {
}
return null;
} catch (e) {
logger.e(e.toString());
loggerService.logError(e.toString());
loggerService.logToFile(e.toString(), type: LogTypeEnum.error);
InfoComponents.showToast(e.toString());
return null;
}
@ -213,7 +200,8 @@ class ScreenDetailsRepoImp implements ScreenDetailsRepo {
);
return genericModel;
} catch (e) {
logger.e(e.toString());
loggerService.logError(e.toString());
loggerService.logToFile(e.toString(), type: LogTypeEnum.error);
InfoComponents.showToast(e.toString());
return null;
}
@ -236,7 +224,8 @@ class ScreenDetailsRepoImp implements ScreenDetailsRepo {
);
return genericModel;
} catch (e) {
logger.e(e.toString());
loggerService.logError(e.toString());
loggerService.logToFile(e.toString(), type: LogTypeEnum.error);
InfoComponents.showToast(e.toString());
return null;
}

@ -1,12 +1,13 @@
import 'dart:developer';
import 'dart:io';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:http/io_client.dart';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:signalr_core/signalr_core.dart';
abstract class SignalrRepo {
Future<void> startHubConnection({
Future<bool?> startHubConnection({
required String deviceIp,
required Function(List<Object?>?) onHubTicketCall,
required Function(dynamic) onHubConfigCall,
@ -18,9 +19,12 @@ abstract class SignalrRepo {
class SignalrRepoImp implements SignalrRepo {
HubConnection? connection;
LoggerService loggerService;
SignalrRepoImp({this.connection, required this.loggerService});
@override
Future<void> startHubConnection({
Future<bool?> startHubConnection({
required String deviceIp,
required Function(List<Object?>?) onHubTicketCall,
required Function(dynamic) onHubConfigCall,
@ -28,42 +32,48 @@ class SignalrRepoImp implements SignalrRepo {
required Function(dynamic) onHubReconnected,
required Function(dynamic) onHubDisconnected,
}) async {
String hubBaseURL = ApiConstants.baseUrlHub;
try {
String hubBaseURL = ApiConstants.baseUrlHub;
if ((connection != null) && (connection!.state == HubConnectionState.connected || connection!.state == HubConnectionState.connecting)) {
return true;
}
connection = HubConnectionBuilder()
.withUrl(
"$hubBaseURL?IPAddress=$deviceIp",
HttpConnectionOptions(
client: IOClient(HttpClient()..badCertificateCallback = (x, y, z) => true),
logging: (level, message) => log(message),
))
.withAutomaticReconnect()
.build();
if ((connection != null) && (connection!.state == HubConnectionState.connected || connection!.state == HubConnectionState.connecting)) {
return;
}
connection = HubConnectionBuilder()
.withUrl(
"$hubBaseURL?IPAddress=$deviceIp",
HttpConnectionOptions(
client: IOClient(HttpClient()..badCertificateCallback = (x, y, z) => true),
logging: (level, message) => log(message),
))
.withAutomaticReconnect()
.build();
connection!.serverTimeoutInMilliseconds = 120000;
connection!.onclose((exception) {
log(exception.toString());
onHubDisconnected(exception);
});
connection!.serverTimeoutInMilliseconds = 120000;
connection!.onclose((exception) {
log(exception.toString());
onHubDisconnected(exception);
});
connection!.onreconnecting((exception) => onHubConnecting(exception));
connection!.onreconnected((connectionId) {
log(connectionId.toString());
onHubReconnected(connectionId);
});
connection!.onreconnecting((exception) => onHubConnecting(exception));
connection!.onreconnected((connectionId) {
log(connectionId.toString());
onHubReconnected(connectionId);
});
connection!.on(ApiConstants.sendQLinePatientCall, (List<Object?>? message) {
onHubTicketCall(message);
});
connection!.on(ApiConstants.sendQLinePatientCall, (List<Object?>? message) {
onHubTicketCall(message);
});
connection!.on(ApiConstants.sendQLineConfig, (List<Object?>? message) {
onHubConfigCall(message);
});
connection!.on(ApiConstants.sendQLineConfig, (List<Object?>? message) {
onHubConfigCall(message);
});
await connection!.start();
await connection!.start();
return true;
} catch (e) {
loggerService.logError(e.toString());
loggerService.logToFile(e.toString(), type: LogTypeEnum.error);
return false;
}
}
bool getHubConnectionState() {

@ -0,0 +1,74 @@
import 'dart:io';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:intl/intl.dart';
import 'package:logger/logger.dart';
import 'package:path_provider/path_provider.dart';
abstract class LoggerService {
Future<void> logToFile(String message, {LogTypeEnum type = LogTypeEnum.data});
Future<void> clearLogs();
void logError(String message);
void logInfo(String message);
}
class LoggerServiceImp implements LoggerService {
Logger logger;
LoggerServiceImp({required this.logger});
@override
Future<void> logToFile(String message, {LogTypeEnum type = LogTypeEnum.data}) async {
try {
final timestamp = DateFormat('yyyy-MM-dd HH:mm:ss a').format(DateTime.now());
final formattedMessage = "[$timestamp] ${type.name.toUpperCase()}: $message";
final dir = await getApplicationDocumentsDirectory();
final logDir = Directory('${dir.path}/logs');
if (!await logDir.exists()) {
await logDir.create(recursive: true);
}
final logFileName = type == LogTypeEnum.error ? AppStrings.errorLogsFileName : AppStrings.dataLogsFileName;
final file = File('${logDir.path}/$logFileName');
await file.writeAsString("$formattedMessage\n", mode: FileMode.append);
} catch (e) {
logger.i('Logging failed: $e');
}
}
@override
Future<void> clearLogs() async {
try {
final dir = await getApplicationDocumentsDirectory();
final logDir = Directory('${dir.path}/logs');
final filesToClear = [
File('${logDir.path}/${AppStrings.errorLogsFileName}'),
File('${logDir.path}/${AppStrings.dataLogsFileName}'),
];
for (var file in filesToClear) {
if (await file.exists()) {
await file.writeAsString('');
}
}
} catch (e) {
logger.e("Failed to clear logs: ${e.toString()}");
}
}
@override
void logError(String message) {
logger.e(message);
}
@override
void logInfo(String message) {
logger.i(message);
}
}

@ -3,8 +3,10 @@ import 'package:flutter_tts/flutter_tts.dart';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:hmg_qline/models/global_config_model.dart';
import 'package:hmg_qline/models/ticket_model.dart';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:hmg_qline/utilities/extensions.dart';
import 'package:logger/logger.dart';
abstract class TextToSpeechService {
Future<void> speechText({
@ -20,8 +22,9 @@ abstract class TextToSpeechService {
class TextToSpeechServiceImp implements TextToSpeechService {
FlutterTts textToSpeechInstance;
LoggerService loggerService;
TextToSpeechServiceImp({required this.textToSpeechInstance});
TextToSpeechServiceImp({required this.textToSpeechInstance, required this.loggerService});
double volume = 1.0;
double pitch = 0.6;
@ -179,17 +182,17 @@ class TextToSpeechServiceImp implements TextToSpeechService {
textToSpeechInstance.setCompletionHandler(onVoiceCompleted);
textToSpeechInstance.setErrorHandler((message) {
logger.e("setErrorHandler: $message\nCompleting the voice for now");
loggerService.logInfo("setErrorHandler: $message\nCompleting the voice for now");
isSpeechCompleted = true;
onVoiceCompleted();
});
textToSpeechInstance.setPauseHandler(() {
logger.e("setPauseHandler");
loggerService.logInfo("setPauseHandler");
});
textToSpeechInstance.setCancelHandler(() {
logger.e("setCancelHandler");
loggerService.logInfo("setCancelHandler");
});
}
}

@ -38,3 +38,8 @@ enum KioskScreenStateEnums {
ticketNumState,
busyState,
}
enum LogTypeEnum {
data,
error,
}

@ -9,6 +9,7 @@ import 'package:hmg_qline/repositories/screen_details_repo.dart';
import 'package:hmg_qline/repositories/signalR_repo.dart';
import 'package:hmg_qline/services/audio_service.dart';
import 'package:hmg_qline/services/cache_service.dart';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:hmg_qline/services/text_to_speech_service.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:hmg_qline/view_models/screen_config_view_model.dart';
@ -19,6 +20,7 @@ class QueuingViewModel extends ChangeNotifier {
final CacheService cacheService;
final AudioService audioService;
final TextToSpeechService textToSpeechService;
final LoggerService loggerService;
QueuingViewModel({
required this.screenDetailsRepo,
@ -26,6 +28,7 @@ class QueuingViewModel extends ChangeNotifier {
required this.cacheService,
required this.audioService,
required this.textToSpeechService,
required this.loggerService,
});
Future<void> initializeQueueingVM() async {
@ -34,9 +37,9 @@ class QueuingViewModel extends ChangeNotifier {
initializeTextToSpeech();
}
Future<void> startHubConnection() async {
Future<bool?> startHubConnection() async {
ScreenConfigViewModel screenConfigViewModel = getIt.get<ScreenConfigViewModel>();
await signalrRepo.startHubConnection(
return await signalrRepo.startHubConnection(
deviceIp: screenConfigViewModel.currentScreenIP,
onHubTicketCall: onHubTicketCall,
onHubConfigCall: onHubConfigCall,
@ -55,6 +58,8 @@ class QueuingViewModel extends ChangeNotifier {
}
Future<void> onHubConfigCall(var response) async {
loggerService.logToFile(response.toString(), type: LogTypeEnum.data);
if (response != null && response.isNotEmpty) {
var data = response.first['data'];
GlobalConfigurationsModel globalConfigurationsModel = GlobalConfigurationsModel.fromJson(
@ -70,6 +75,18 @@ class QueuingViewModel extends ChangeNotifier {
}
}
Future<void> onHubTicketCall(List<Object?>? response) async {
loggerService.logToFile(response.toString(), type: LogTypeEnum.data);
log("onHubTicketCall: $response");
log("isCallingInProgress: $isCallingInProgress");
if (response != null && response.isNotEmpty) {
TicketDetailsModel ticketDetailsModel = TicketDetailsModel.fromJson(response.first as Map<String, dynamic>);
addNewTicket(ticketDetailsModel);
}
}
Future<void> onHubReconnected(var response) async {
log("onHubConnected: $response");
ScreenConfigViewModel screenConfigViewModel = getIt.get<ScreenConfigViewModel>();
@ -124,15 +141,6 @@ class QueuingViewModel extends ChangeNotifier {
});
}
Future<void> onHubTicketCall(List<Object?>? response) async {
log("onHubTicketCall: $response");
log("isCallingInProgress: $isCallingInProgress");
if (response != null && response.isNotEmpty) {
TicketDetailsModel ticketDetailsModel = TicketDetailsModel.fromJson(response.first as Map<String, dynamic>);
addNewTicket(ticketDetailsModel);
}
}
int itemAlreadyAvailableAtIndex({required TicketDetailsModel ticketToSearch, required List<TicketDetailsModel> listToSearchIn}) {
int isFoundAt = -1;
try {

@ -14,11 +14,11 @@ import 'package:hmg_qline/models/weathers_widget_model.dart';
import 'package:hmg_qline/repositories/screen_details_repo.dart';
import 'package:hmg_qline/services/cache_service.dart';
import 'package:hmg_qline/services/connectivity_service.dart';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:hmg_qline/utilities/extensions.dart';
import 'package:hmg_qline/view_models/queuing_view_model.dart';
import 'package:hmg_qline/views/view_helpers/info_components.dart';
import 'package:intl/intl.dart';
import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart';
// import 'package:timezone/browser.dart' as tz;
@ -26,11 +26,13 @@ class ScreenConfigViewModel extends ChangeNotifier {
final ScreenDetailsRepo screenDetailsRepo;
final CacheService cacheService;
final ConnectivityService connectivityService;
final LoggerService loggerService;
ScreenConfigViewModel({
required this.screenDetailsRepo,
required this.cacheService,
required this.connectivityService,
required this.loggerService,
});
Future<void> initializeScreenConfigVM() async {
@ -40,12 +42,6 @@ class ScreenConfigViewModel extends ChangeNotifier {
getTheWidgetsConfigurationsEveryMidnight();
}
// Future<void> initializeTimezone() async {
// await tz.initializeTimeZone();
// var detroit = tz.getLocation('America/Detroit');
// var now = tz.TZDateTime.now(detroit);
// }
Future<void> waitForIPAndInitializeConfigVM() async {
while (currentScreenIP == "") {
await getCurrentScreenIP();
@ -123,7 +119,8 @@ class ScreenConfigViewModel extends ChangeNotifier {
GlobalConfigurationsModel? response = await screenDetailsRepo.getGlobalScreenConfigurations(ipAddress: currentScreenIP);
if (response == null) {
logger.e("response was: $response");
loggerService.logError("response was: $response");
updateViewState(ViewState.error);
return;
}
updateGlobalConfigurationsModel(value: response);
@ -216,11 +213,17 @@ class ScreenConfigViewModel extends ChangeNotifier {
if (!isHubConnected) {
log("Hub is Not Connected!, I will try to reconnect now.");
QueuingViewModel queuingViewModel = getIt.get<QueuingViewModel>();
queuingViewModel.startHubConnection();
bool? status = await queuingViewModel.startHubConnection();
if (status != false) {
updateIsHubConnected(true);
}
} else {
log("Hub is Connected!");
}
// log("Data Before Check : ${prayersWidgetModel.fajr}");
// log("Data Before Check : ${globalConfigurationsModel.isPrayerTimeReq}");
if (globalConfigurationsModel.isPrayerTimeReq && prayersWidgetModel.fajr == null) {
await getPrayerDetailsFromServer();
}
@ -386,13 +389,13 @@ class ScreenConfigViewModel extends ChangeNotifier {
patientId: patientId,
);
if (response == null || response.messageStatus != 1) {
logger.e("response null from createTicketFromKiosk");
loggerService.logError("response null from createTicketFromKiosk");
return null;
}
return response.data;
} catch (e) {
InfoComponents.showToast(e.toString());
logger.i(e.toString());
loggerService.logError(e.toString());
return null;
}
}
@ -404,10 +407,10 @@ class ScreenConfigViewModel extends ChangeNotifier {
qTypeEnum: globalConfigurationsModel.qTypeEnum,
);
if (response == null || response.messageStatus != 1) {
logger.e("response null from acknowledgeTicket");
loggerService.logError("response null from acknowledgeTicket");
return;
} else {
logger.i("response from acknowledgeTicket ${response.data}");
loggerService.logInfo("response from acknowledgeTicket ${response.data}");
}
}
@ -418,10 +421,10 @@ class ScreenConfigViewModel extends ChangeNotifier {
callTypeEnum: callTypeEnum,
);
if (response == null || response.messageStatus != 1) {
logger.e("response null from acknowledgeTicket");
loggerService.logError("response null from acknowledgeTicketForAppointmentOnly");
return;
} else {
logger.i("response from acknowledgeTicket ${response.data}");
loggerService.logInfo("response from acknowledgeTicketForAppointmentOnly ${response.data}");
}
}
@ -463,7 +466,7 @@ class ScreenConfigViewModel extends ChangeNotifier {
qrViewController = controller;
controller.scannedDataStream.listen((scanData) async {
logger.i("Found: ${scanData.code} and isGeneratingTicket: $isGeneratingTicket");
loggerService.logInfo("Found: ${scanData.code} and isGeneratingTicket: $isGeneratingTicket");
if (isGeneratingTicket) return;
await validateAndGenerateTicketFromQR(data: scanData, onFailure: onFailure, onSuccess: onSuccess);
});

@ -133,13 +133,21 @@ Widget counterNoText({required int counterNo, required bool isRoomNoRequired, re
);
}
Widget noPatientInQueue({required String text, required String fontName, required String roomText, required bool isRoomNoRequired, required bool isForRoomLevel, required int counterNo}) {
Widget intimationWidget({
required String text,
required String fontName,
required String roomText,
required bool isRoomNoRequired,
required bool isForRoomLevel,
required int counterNo,
bool isForError = false,
}) {
Widget noPatientText = Center(
child: AppText(
text,
fontFamily: fontName,
textAlign: TextAlign.center,
fontSize: SizeConfig.getWidthMultiplier() * 9,
fontSize: isForError ? SizeConfig.getWidthMultiplier() * 7 : SizeConfig.getWidthMultiplier() * 9,
),
);
if (isForRoomLevel) {

@ -59,11 +59,12 @@ class KioskMainScreen extends StatelessWidget {
bool isLanguageConfigAvailable = screenConfigViewModel.globalConfigurationsModel.kioskLanguageConfigList != null;
if (!isLanguageConfigAvailable) {
return noPatientInQueue(
return intimationWidget(
text: AppStrings.configurationIssueContactAdmin,
fontName: AppStrings.fontNamePoppins,
isForRoomLevel: false,
isRoomNoRequired: false,
isForError: true,
counterNo: 0,
roomText: '',
);
@ -105,11 +106,12 @@ class KioskMainScreen extends StatelessWidget {
Widget configurationWidgetIssue() {
return Center(
child: noPatientInQueue(
child: intimationWidget(
text: AppStrings.configurationIssueContactAdmin,
fontName: AppStrings.fontNamePoppins,
isForRoomLevel: false,
isRoomNoRequired: false,
isForError: true,
counterNo: 0,
roomText: '',
),

@ -40,9 +40,20 @@ class MainQueueScreen extends StatelessWidget {
text = AppStrings.awaitingArrivalAr;
fontFamily = AppStrings.fontNameCairo;
}
if (queuingViewModel.currentTickets.isEmpty) {
if (screenConfigViewModel.state == ViewState.error) {
widget = intimationWidget(
text: AppStrings.configurationIssueContactAdmin,
fontName: AppStrings.fontNamePoppins,
isForRoomLevel: false,
isRoomNoRequired: false,
isForError: true,
counterNo: 0,
roomText: '',
);
} else if (queuingViewModel.currentTickets.isEmpty) {
bool isForRoomLevel = screenConfigViewModel.globalConfigurationsModel.screenTypeEnum == ScreenTypeEnum.roomLevelScreen;
widget = noPatientInQueue(
widget = intimationWidget(
text: text,
fontName: fontFamily,
isForRoomLevel: isForRoomLevel,

@ -385,7 +385,7 @@ packages:
source: hosted
version: "1.1.0"
path_provider:
dependency: transitive
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"

@ -52,6 +52,7 @@ dependencies:
logger: ^2.4.0
fluttertoast: ^8.2.8
qr_code_scanner_plus: ^2.0.10+1
path_provider: ^2.1.5
# esc_pos_printer: ^4.0.0 # Ensure you are using the latest version
# esc_pos_utils: ^1.0.0

Loading…
Cancel
Save