import { ApplicationRef, Injectable, NgZone, OnDestroy, OnInit, } from "@angular/core"; import { BLE } from "@ionic-native/ble/ngx"; import { LoadingController, ViewWillEnter } from "@ionic/angular"; import { AlertController } from "@ionic/angular"; import { TranslateService } from "@ngx-translate/core"; import { container } from "../models/container.model"; import { Settings } from "../models/settings.model"; import { CountService } from "./count.service"; import { PouchdbService } from "./pouchdb.service"; const readCharacteristic = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"; const writeCharacteristic = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"; const service = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; const RESET = new TextEncoder().encode("RESET").buffer; const minusCNT = new TextEncoder().encode("CNT-").buffer; @Injectable({ providedIn: "root", }) export class Connect2frameService implements OnDestroy, ViewWillEnter, OnInit { initializeResult; public scannedDevices: any[] = []; //a list of all available BT devices public IsConnected: boolean = false; //Connection state public IsEnabled: boolean; //Bluetooth enabled yes/no private currentRX: string = ""; //holds part of the command public currentDev: string = ""; public batteryLevel: string = ""; private readerSub; public isLoading: boolean = false; public lastValue: number = 0; constructor( private ble: BLE, //Bluetooth Plugin, handling the connection to the frame private AppRef: ApplicationRef, public countService: CountService, private zone: NgZone, private translateService: TranslateService, private loadCtrl: LoadingController, private alertCtrl: AlertController, private pouch: PouchdbService ) { } ngOnInit() {} ionViewWillEnter() { } ngOnDestroy() { this.disconnect(); } //Scans for available Bluetooth Devices scan() { let list = []; console.log(this.IsEnabled); if (this.IsEnabled) { this.ble.scan([], 5).subscribe((device) => { this.zone.run(() => { this.scannedDevices.push({ id: device.id, name: device.name }); }); }); } } //Sends RESET Command to the Frame --> Frame resets the counting value to 0 resetFrame() { return new Promise((resolve, reject) => { if (this.IsConnected === true) { return this.ble .writeWithoutResponse( this.currentDev, service, writeCharacteristic, RESET ) .then(() => { this.countService.lastCountValue = 0; this.lastValue = 0; resolve(null); }); } }); } resetCurrentContainer() { let startedAt = this.countService.selectedContainer.countedValue; let index = this.countService.containers.indexOf( this.countService.selectedContainer ); this.countService.containers[index] = new container(index + 1); this.countService.selectedContainer = this.countService.containers[index]; this.loadCtrl .create({ backdropDismiss: false, id: "resetContainer", keyboardClose: false, message: this.translateService.instant("RESET_CONTAINER_M"), }) .then(async (loader) => { loader.present(); let difference = this.countService.currentCount.count - startedAt; console.log(difference); for (let i = 0; i <= difference; i++) { await this.minus(); } loader.dismiss(); }); } //closes the bluetooth connection to the Frame disconnect() { return new Promise((resolve, reject) => { this.resetFrame().then(() => { this.ble .disconnect(this.currentDev) .then((success) => { this.scannedDevices = []; this.IsConnected = false; this.currentDev = ""; resolve(null); }) .catch((fail) => { reject(null); }); }); }); } //connects to the selected Bluetooth Device, Error Callback gets executed if the bluetooth connection fails at any point. connect(id: string) { return new Promise((resolve, reject) => { if (this.IsEnabled) { let loading = this.loadCtrl .create({ message: this.translateService.instant("CONNECTING") + "...", }) .then((loader) => { loader.present(); }); this.ble.connect(id).subscribe( (data) => { this.loadCtrl.dismiss(loading); console.log(data); // this.alertCtrl.create({ // header: this.translateService.instant("SUCCESS"), // message: this.translateService.instant("CONNECTION SUCCEEDED"), // buttons: ["OK"], // }).then(alert=>{ // alert.present(); // }); this.currentDev = id; this.scannedDevices = []; this.IsConnected = true; this.AppRef.tick(); /*this.resetFrame().then(()=>{ this.startReading(); });*/ this.startReading(); resolve(true); }, (error) => { this.alertCtrl .create({ header: this.translateService.instant("ERROR"), message: this.translateService.instant("COULDN'T CONNECT"), buttons: ["OK"], }) .then((alert) => { alert.present(); this.loadCtrl.dismiss(loading); }); this.zone.run(() => { this.IsConnected = false; }); reject(); } ); } else { this.alertCtrl .create({ header: this.translateService.instant("BLUETOOTH DISABLED"), message: this.translateService.instant( "ENABLE BLUETOOTH AND TRY AGAIN" ), }) .then((alert) => { alert.present(); }); } }); } //Starts Notification, where the callback will be executed everytime a new value is received from the frame. startReading() { this.readerSub = this.ble .startNotification( this.currentDev, "6e400001-b5a3-f393-e0a9-e50e24dcca9e", "6e400003-b5a3-f393-e0a9-e50e24dcca9e" ) .subscribe( (value) => { this.readValue(new TextDecoder().decode(value[0])); }, (error) => { console.log(error); } ); } //checks for delimiter and handles parts of a single command. readValue(stringPart: string) { if (stringPart.includes("\n")) { var position = stringPart.indexOf("\n"); this.currentRX = this.currentRX + stringPart.slice(0, position); console.log(this.currentRX); this.putValue(this.currentRX); if (position + 1 <= stringPart.length) { this.currentRX = stringPart.slice(position + 2); } } else { this.currentRX = this.currentRX + stringPart; } } //Pushes the data to the UI or Resets it if there was a reset either by the app or the frame putValue(string: string) { if (string.includes("RESET;M")) { this.countService.resetUI(); this.countService.resetContainers(); } else { if (!string.includes("RESET") && !string.includes("ACK;CNT-")) { let value = parseInt(string.slice(12, 17)); let battery = parseInt(string.slice(18,21)); this.batteryLevel = battery.toString(); if (this.lastValue != value) { let difference = value - this.lastValue; if (this.countService.singleMode) { this.zone.run(() => { this.putContainerValue(this.countService.currentCount.count + difference); this.countService.currentCount.count = this.countService.currentCount.count + difference; }); this.countService.check(); } else { this.zone.run(() => { console.log("assigned a new Value to currentCount"); console.log(value, this.lastValue); this.countService.currentCount.count = this.countService.currentCount.count + difference * 2; this.putContainerValue(this.countService.currentCount.count); }); } this.lastValue = value; } } } } //Starts State Notifications where callback gets executed whenever Bluetooth gets disabled or enabled. initBLE() { this.ble.startStateNotifications().subscribe((state) => { if (state === "on") { this.IsEnabled = true; } else { if ((this.IsConnected = true)) this.IsEnabled = false; } }); } //sends request to Frame to reduce the counted amount by 1 async minus() { return new Promise((resolve, reject) => { this.ble .write(this.currentDev, service, writeCharacteristic, minusCNT) .then(() => { console.log("COUNT REDUCED BY 1"); resolve(null); }); }); } selectContainer(container: container) { this.countService.selectedContainer.save(); this.pauseCountingFrame().then(() => { this.startReading(); this.countService.selectedContainer = container; this.countService.selectedContainer.countStartedAt = this.countService.currentCount.count; }); } putContainerValue(value: number) { this.countService.selectedContainer.newValue(value); } pauseCountingFrame() { return new Promise((resolve, reject) => { let pausedAt = this.countService.currentCount.count; this.ble .stopNotification( this.currentDev, "6e400001-b5a3-f393-e0a9-e50e24dcca9e", "6e400003-b5a3-f393-e0a9-e50e24dcca9e" ) .then(() => { this.alertCtrl .create({ header: this.translateService.instant("CF_PAUSE_HEADER"), message: this.translateService.instant("CF_PAUSE_MESSAGE"), backdropDismiss: false, keyboardClose: false, buttons: [ { text: this.translateService.instant("CONTINUE_COUNT_PROCESS"), role: "confirm", handler: () => { this.loadCtrl .create({ id: "pauseLoader", message: this.translateService.instant( "RESETING_COUNT_MESSAGE" + "..." ), backdropDismiss: false, keyboardClose: false, }) .then(async (loader) => { loader.present(); let difference = this.countService.currentCount.count - pausedAt; for (let i = 0; i < difference; i++) { await this.minus().then(() => { console.log(this.countService.currentCount.count); }); } this.loadCtrl.dismiss(null, null, "pauseLoader"); console.log("loader dismissed"); resolve(null); }); }, }, ], }) .then((alert) => { alert.present(); }); }); }); } addContainer() { this.countService.selectedContainer.save(); this.pauseCountingFrame().then(() => { this.startReading(); this.zone.run(() => { this.countService.containers.push( new container( this.countService.containers.length + 1, this.countService.currentCount.count ) ); this.countService.selectedContainer = this.countService.containers[this.countService.containers.length - 1]; console.log(this.countService.containers); }); }); } }