diff --git a/example/blue/pubspec.lock b/example/blue/pubspec.lock new file mode 100644 index 0000000..e1077d2 --- /dev/null +++ b/example/blue/pubspec.lock @@ -0,0 +1,385 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + charset_converter: + dependency: "direct main" + description: + name: charset_converter + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.3" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.2" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + esc_pos_bluetooth: + dependency: "direct main" + description: + path: "../.." + relative: true + source: path + version: "0.2.8" + esc_pos_utils: + dependency: "direct main" + description: + name: esc_pos_utils + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.6" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "5.2.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_bluetooth_basic: + dependency: transitive + description: + name: flutter_bluetooth_basic + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + gbk_codec: + dependency: transitive + description: + name: gbk_codec + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2" + hex: + dependency: transitive + description: + name: hex + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+3" + image: + dependency: "direct main" + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.17" + intl: + dependency: "direct main" + description: + name: intl + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + oktoast: + dependency: "direct main" + description: + name: oktoast + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.2" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.18" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+2" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+4" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.4+1" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.4" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.13" + qr: + dependency: transitive + description: + name: qr + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.0" + rxdart: + dependency: transitive + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.23.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.3" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.0" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "4.5.1" +sdks: + dart: ">=2.10.0-110 <2.11.0" + flutter: ">=1.12.13+hotfix.5 <2.0.0" diff --git a/lib/src/printer_bluetooth_manager.dart b/lib/src/printer_bluetooth_manager.dart index ba78b32..c7c30f9 100644 --- a/lib/src/printer_bluetooth_manager.dart +++ b/lib/src/printer_bluetooth_manager.dart @@ -1,16 +1,19 @@ /* * esc_pos_bluetooth * Created by Andrey Ushakov - * + * * Copyright (c) 2019-2020. All rights reserved. * See LICENSE for distribution and usage details. */ import 'dart:async'; import 'dart:io'; + import 'package:esc_pos_utils/esc_pos_utils.dart'; -import 'package:rxdart/rxdart.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter_bluetooth_basic/flutter_bluetooth_basic.dart'; +import 'package:rxdart/rxdart.dart'; + import './enums.dart'; /// Bluetooth printer @@ -39,11 +42,16 @@ class PrinterBluetoothManager { BehaviorSubject.seeded([]); Stream> get scanResults => _scanResults.stream; + List _bufferedBytes = []; + int _queueSleepTimeMs = 20; + int _chunkSizeBytes = 20; + int _connectionTimeOut = 10; + Future _runDelayed(int seconds) { return Future.delayed(Duration(seconds: seconds)); } - void startScan(Duration timeout) async { + Future startScan(Duration timeout) async { _scanResults.add([]); _bluetoothManager.startScan(timeout: timeout); @@ -63,22 +71,36 @@ class PrinterBluetoothManager { }); } - void stopScan() async { + Future stopScan() async { await _bluetoothManager.stopScan(); + await _isScanningSubscription?.cancel(); } void selectPrinter(PrinterBluetooth printer) { _selectedPrinter = printer; + _bluetoothManager.state.listen((state) async { + switch (state) { + case BluetoothManager.CONNECTED: + _isConnected = true; + print('CONNECTED STATE'); + print('CONNECTED STATE'); + break; + case BluetoothManager.DISCONNECTED: + _isConnected = false; + print('DISCONNECTED STATE'); + print('DISCONNECTED STATE'); + break; + default: + break; + } + print('BluetoothManager.STATE => $state'); + }); } - Future writeBytes( + Future _connectBluetooth( List bytes, { - int chunkSizeBytes = 20, - int queueSleepTimeMs = 20, + int timeout = 5, }) async { - final Completer completer = Completer(); - - const int timeout = 5; if (_selectedPrinter == null) { return Future.value(PosPrintResult.printerNotSelected); } else if (_isScanning.value) { @@ -86,74 +108,166 @@ class PrinterBluetoothManager { } else if (_isPrinting) { return Future.value(PosPrintResult.printInProgress); } - - _isPrinting = true; - // We have to rescan before connecting, otherwise we can connect only once await _bluetoothManager.startScan(timeout: Duration(seconds: 1)); await _bluetoothManager.stopScan(); - // Connect await _bluetoothManager.connect(_selectedPrinter._device); + final result = await _checkConnectionState(); + return result; + } - // Subscribe to the events - _bluetoothManager.state.listen((state) async { - switch (state) { - case BluetoothManager.CONNECTED: - // To avoid double call - if (!_isConnected) { - final len = bytes.length; - List> chunks = []; - for (var i = 0; i < len; i += chunkSizeBytes) { - var end = (i + chunkSizeBytes < len) ? i + chunkSizeBytes : len; - chunks.add(bytes.sublist(i, end)); - } + Future _writeRequest(timeout) async { + final Completer completer = Completer(); + if (_bufferedBytes.isNotEmpty) { + await _writePending(); + _runDelayed(timeout).then((dynamic v) async { + if (_isPrinting) { + _isPrinting = false; + } + completer.complete(PosPrintResult.success); + }); + } + return completer.future; + } - for (var i = 0; i < chunks.length; i += 1) { - await _bluetoothManager.writeData(chunks[i]); - sleep(Duration(milliseconds: queueSleepTimeMs)); - } + Future printTicket(Ticket ticket, + {int chunkSizeBytes = 20, + int queueSleepTimeMs = 20, + int timeout = 5, + int connectionTimeOut = 10}) async { + if (ticket == null || ticket.bytes.isEmpty) { + return Future.value(PosPrintResult.ticketEmpty); + } + _bufferedBytes = []; + _bufferedBytes = ticket.bytes; + _queueSleepTimeMs = queueSleepTimeMs; + _chunkSizeBytes = chunkSizeBytes; + _connectionTimeOut = connectionTimeOut; + if (_isConnected) { + return await _writeRequest(timeout); + } else { + final result = await connect(ticket.bytes, timeout); + if (result.msg == "Success") { + return await _writeRequest(timeout); + } else { + return result; + } + } + } + Future printLabel(List bytes, + {int chunkSizeBytes = 20, + int queueSleepTimeMs = 20, + int timeout = 5, + int connectionTimeOut = 10}) async { + if (bytes == null || bytes.isEmpty) { + return Future.value(PosPrintResult.ticketEmpty); + } + _bufferedBytes = []; + _bufferedBytes = bytes; + _queueSleepTimeMs = queueSleepTimeMs; + _chunkSizeBytes = chunkSizeBytes; + _connectionTimeOut = connectionTimeOut; + if (_isConnected) { + return await _writeRequest(timeout); + } else { + final result = await connect(bytes, timeout); + if (result.msg == "Success") { + return await _writeRequest(timeout); + } else { + return result; + } + } + } + + Future _checkConnectionState() async { + Timer _stateTimer; + int _start = _connectionTimeOut; + final Completer completer = Completer(); + const oneSec = Duration(seconds: 1); + _stateTimer = Timer.periodic( + oneSec, + (Timer timer) { + if (_start == 0 || _isConnected) { + timer.cancel(); + print('ENDTIME'); + print(_isConnected); + if (_isConnected) { + _stateTimer?.cancel(); completer.complete(PosPrintResult.success); + } else { + _stateTimer?.cancel(); + completer.complete(PosPrintResult.timeout); } - // TODO sending disconnect signal should be event-based - _runDelayed(3).then((dynamic v) async { - await _bluetoothManager.disconnect(); - _isPrinting = false; - }); - _isConnected = true; - break; - case BluetoothManager.DISCONNECTED: - _isConnected = false; - break; - default: - break; - } - }); + } else { + _start--; + } + }, + ); + return completer.future; + } - // Printing timeout - _runDelayed(timeout).then((dynamic v) async { - if (_isPrinting) { - _isPrinting = false; - completer.complete(PosPrintResult.timeout); - } - }); + Future connect(bytes, timeout) async { + print("CONNECTING ON PRINT"); + print("CONNECTING ON PRINT"); + final result = await _connectBluetooth( + bytes, + timeout: timeout, + ); + return result; + } + + Future disconnect(timeout) async { + final Completer completer = Completer(); + try { + print('PENDING DISCONNECTED'); + await Future.delayed(Duration(seconds: timeout)); + await _bluetoothManager.disconnect(); + Timer _stateTimer; + int _start = _connectionTimeOut; + const oneSec = Duration(seconds: 1); + _stateTimer = Timer.periodic( + oneSec, + (Timer timer) { + print("START: $_start"); + print("STATUS: $_isConnected"); + if (_start == 0 || !_isConnected) { + timer.cancel(); + if (!_isConnected) { + _stateTimer?.cancel(); + print('SUCCESS DISCONNECT'); + completer.complete(PosPrintResult.success); + } else { + _stateTimer?.cancel(); + completer.complete(PosPrintResult.timeout); + } + } else { + _start--; + } + }, + ); + } catch (err) { + print(err); + completer.complete(PosPrintResult.timeout); + } return completer.future; } - Future printTicket( - Ticket ticket, { - int chunkSizeBytes = 20, - int queueSleepTimeMs = 20, - }) async { - if (ticket == null || ticket.bytes.isEmpty) { - return Future.value(PosPrintResult.ticketEmpty); + Future _writePending() async { + final len = _bufferedBytes.length; + List> chunks = []; + for (var i = 0; i < len; i += _chunkSizeBytes) { + var end = (i + _chunkSizeBytes < len) ? i + _chunkSizeBytes : len; + chunks.add(_bufferedBytes.sublist(i, end)); } - return writeBytes( - ticket.bytes, - chunkSizeBytes: chunkSizeBytes, - queueSleepTimeMs: queueSleepTimeMs, - ); + _isPrinting = true; + for (var i = 0; i < chunks.length; i += 1) { + await _bluetoothManager.writeData(chunks[i]); + sleep(Duration(milliseconds: _queueSleepTimeMs)); + } + _isPrinting = false; + _bufferedBytes = []; } } diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..bf375cd --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,252 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.13" + args: + dependency: transitive + description: + name: args + url: "https://pub.dartlang.org" + source: hosted + version: "1.6.0" + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.5.0-nullsafety.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.3" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0-nullsafety.3" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.2" + esc_pos_utils: + dependency: "direct main" + description: + name: esc_pos_utils + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.6" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_bluetooth_basic: + dependency: "direct main" + description: + name: flutter_bluetooth_basic + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.5" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + gbk_codec: + dependency: transitive + description: + name: gbk_codec + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.2" + hex: + dependency: transitive + description: + name: hex + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+3" + image: + dependency: transitive + description: + name: image + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.17" + json_annotation: + dependency: transitive + description: + name: json_annotation + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10-nullsafety.1" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.1" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.4" + rxdart: + dependency: "direct main" + description: + name: rxdart + url: "https://pub.dartlang.org" + source: hosted + version: "0.23.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0-nullsafety.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0-nullsafety.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0-nullsafety.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0-nullsafety.1" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.19-nullsafety.2" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0-nullsafety.3" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0-nullsafety.3" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "4.5.1" +sdks: + dart: ">=2.10.0-110 <2.11.0" + flutter: ">=1.12.0 <2.0.0"