bobaflu - الملحقات البرمجة على الرفرفة


سوف تركز هذه المقالة على تنفيذ عميل المحمول Flutter.


أي عميل المحمول؟


وصف المنشور السابق نظام ملحقات البرامج:
bobaoskit - الملحقات ، dnssd و WebSocket .


التماثلية لبرنامج ملحق هو كائن حقيقي. المصباح الكهربائي ، المفتاح ، مشغل أسطوانات مدمجة / كاسيت ، مشغل راديو ، منظم حرارة ، مستشعر درجة الحرارة ، مستشعر الحركة ، إلخ ... يتم تحديد مجموعة من الملحقات حسب الخيال ورمز البرنامج. يمكنك تنفيذ ما لا يقل عن رقعة الشطرنج. بالنسبة إلى هذه اللوحة ، يجب أن يكون لديك نقل ( control ) لحقل control ، والذي يأخذ كائن { from: "e2", to: "e4" } على سبيل المثال ، وحقول الخدمة لإعادة تعيين الأرقام ، وما إلى ذلك ... سيقوم البرنامج النصي الملحق بمعالجة طلب التحكم في حقل move ، وقبول القرار هو ما إذا كان من الممكن تحريك الرقم ، وسيعود (أو لا) الحالة مع موضع الأرقام في جميع أنحاء الحقل.


أنواع الملحقات المعتمدة حاليًا مع الحد الأدنى من الوظائف هي كما يلي: "التبديل" ، "مستشعر درجة الحرارة" ، "ترموستات" ، "مشغل راديو".


حول لعبة الشطرنج ، لن يكون هناك مزيد من النقاش. إذا كانت مثيرة للاهتمام وفي هذه الحالة ، مرحبا بكم في القط.


حتى bobaoskit.worker قيد التشغيل. توجد كائنات ملحقة في ذاكرة الكمبيوتر ، ويمكنك قراءة المعلومات المتعلقة بها ، ويمكنك إرسال طلب JSON يدويًا إلى منفذ WebSocket ، ومشاهدة الأحداث الواردة.


بالنسبة للإدارة ، لقد صنعت أبسط تطبيقات الهاتف المحمول.


لماذا ترفرف؟


على مدى العامين الماضيين ، كانت فكرة تعيش في رأسي بنشاط لدراسة البرمجة للأجهزة المحمولة. منذ أن كتبت في JavaScript ، قمت بدراسة الحلول التي تسمح لك بعدم تعلم لغة برمجة جديدة.


Appcelerator بدأ الدراسة معه. إذا لم تتغير الذاكرة ، تكون SDK مفتوحة ، ولكن IDE مع التعريفات المختلفة.
NativeScript لقد قمت بالفعل بإنشاء تطبيق بسيط يعرض قائمة بالصور. لم يذهب أبعد من ذلك.
ReactNative أطول اعتداء على الأطر المذكورة حتى الآن. التحدي الأكبر هو البدء. نظرت إلى الدورة. في البداية كان الأمر واضحًا ومثيرًا للاهتمام ، كما اتضح. لكن redux وبعد فشل التغلب. ثم حاول بانتظام البدء في الكتابة ، لكن redux عنيد لم يسمح لنفسه أن يتغلب.


كنتيجة لذلك ، لم أكن مرفقة بأي قرار بعد ذلك (نهاية عام 2016). ربما لأنه لم يكن هناك مهمة محددة ، ربما لأسباب أخرى.


أقرب إلى خريف الماضي (2018) ، كان العمل جارٍ بالفعل على sdk لملحقات البرامج. بطبيعة الحال ، تحتاج إلى تطبيق للهاتف المحمول. بدأ كل شيء مع mdns. مرة واحدة في وقت فراغي لقد قمت بتحديث ReactNative ، ووجدت البرنامج المساعد react-native-zeroconf ، الذي أنشأ التطبيق. وفقا للتعليمات ، تثبيت ، أدلى link ، أطلقت. تم بدء تشغيل تطبيق تصحيح Expo ، الذي لا يدعم الوحدات النمطية الأصلية ، وبالتالي ، لم يعمل البرنامج المساعد mdns. في هذه المرحلة ، لم يكن هناك ما يكفي من وقت الفراغ لإنشاء تطبيق نظيف (بدون معرض) للتفاعل الأصلي واختباره. تم تأجيل العمل لبضعة أشهر.


في الوقت نفسه ، ظهرت المزيد والمزيد من المواد حول flutter على الشبكة. لقد قمت بتثبيت نفسي. التثبيت بسيط: git clone وأضف إلى PATH . يقوم الباقي بالفعل بإعداد Android SDK / Xcode (في حالتي ، تم تكوين Android SDK لفترة طويلة. لا يمكنني تطوير نظام iOS ، لأنني لست مستخدم نظام MacOS) و Dart SDK (يمكنك تثبيته بشكل منفصل ، ولكن ليس بالضرورة ، لأنه جزء من الرفرفة).


مبدأ / مخطط العمل


  • عند بدء التشغيل ، يبحث التطبيق عن خدمات _bobaoskit._tcp على الشبكة المحلية باستخدام البرنامج المساعد flutter_mdns . هناك عدة إصدارات من هذا البرنامج المساعد ، جميعها تأخذ جذورها من النسخة المنشورة ، لكنها غير متوافقة مع الإصدارات الجديدة من Dart SDK ، على التوالي ، العديد من التوافقات ذات الشوكة والإضافة. لقد اخترت هذا الإصدار لأن الآخرين لم يحلوا مضيفي العديد من الخدمات المكتشفة في وقت واحد.
    عند الكشف والتصميم (onResolve) ، تتم إضافة المضيف إلى القائمة.
    الصفحة التي تحتوي على قائمة بالخدمات المكتشفة هي StatefulWidget ، على التوالي ، عندما تكتشف / تفقد الخدمات ، يتم setState() {...}
  • عند اختيار مضيف من القائمة ، يتم إنشاء صفحة جديدة (أيضًا StatefulWidget ) ، والتي يتم إرسال host port الخدمة المحددة إليها.
    يتم BobaosKit كائن BobaosKit المسؤول عن الاتصالات. تتم معالجة الردود من خلال الاسترجاعات ، كما بينما أنا لم أدرس الكثير من دارت غير متزامن. ولكن وفقًا للوثائق الممسوحة ضوئيًا ، فإن Futures تعد بمثابة تناظر Promise في JS.
    يتم تسجيل وظائف للأحداث الواردة (لا توجد ردود). EventEmitter عن EventEmitter لـ Dart هنا. كتبت لي بسيط جدا.

  void registerListener(String name, Function cb) { this._events.add(new BobaosKitCallback(name, cb)); } void removeAllListeners() { this._events = []; } void emitEvent(String name, dynamic params) { // call all listeners List<BobaosKitCallback> foundCallbacks = this._events.where((t) => t.name == name).toList(); foundCallbacks.forEach((f) => f.cb(params)); } ... ... void listen() { this.ws.listen((text) { var json = jsonDecode(text); if (json.containsKey('response_id')) { .... } else { //   response_id -  this.emitEvent(json['method'], json['payload']); } }); } 

الأحداث الواردة - إذا تمت إزالة الملحق ، تمت إضافة الحالة المحدثة. أو إذا تمت إزالة جميع الملحقات ( clear accessories ).


وظائف مسجلة - لتحديث القوائم ، الحاجيات لهذه الأحداث.


  • يتم إرسال طلب للحصول على معلومات حول جميع الملحقات.

يتم إنشاء كائن AccessoryInfo لكل ملحق:


 import 'package:scoped_model/scoped_model.dart'; // AccessoryInfo extends Model // so, when accessory value is updated it descends down to // all widgets inside ScopedModelDescendant class AccessoryInfo extends Model { dynamic id; dynamic type; String name; String job_channel; List control; List status; bool selected; Map<dynamic, dynamic> currentState; AccessoryInfo(Map<dynamic, dynamic> obj) { this.id = obj['id']; this.type = obj['type']; this.name = obj['name']; this.job_channel = obj['job_channel']; this.control = obj['control']; this.status = obj['status']; this.currentState = {}; } void updateCurrentState(key, value) { currentState[key] = value; notifyListeners(); } void notify() { notifyListeners(); } } 

هذا الكائن هو بالفعل نموذج. في البداية ، كتبت StatefulWidget و setState() {} كل مكان ، لكن setState() {} يعمل فقط مع عنصر واجهة مستخدم تم تسجيل المستمعين داخله. ولكن بالنسبة لإدارة الملحقات التفصيلية ، قمتُ في البداية بإنشاء صفحات Stateful جديدة ، ولاحظت أن الحالة لا يتم تحديثها. كحل - تستخدم ScopedModel .


بعد تلقي قائمة الملحقات ، نرسل لكل حالة طلبًا للحالة ونضيفها إلى القائمة List <AccessoryInfo> . استدعاء setState() {} ، وبالتالي إضافة ملحق معتمد إلى الواجهة. يتم تعريف أنواع الملحقات المدعومة في ListView.builder وفي ./lib/widgets/*.dart . يدعم حاليا switch/temperature sensor/radio player/thermostat . العمل الرئيسي في المستقبل هو إضافة أدوات جديدة وتحسين عناصر واجهة المستخدم الحالية.


  • الآن حول كيفية إنشاء عناصر منفصلة لكل ملحق. على سبيل المثال ، النظر في التبديل.

  @override Widget build(BuildContext context) { return new ScopedModel<AccessoryInfo>( model: info, child: ScopedModelDescendant<AccessoryInfo>( builder: (context, child, model) { var cardColor = Theme.of(context).cardColor; dynamic switchState = model.currentState['state']; if (switchState is bool) { if (switchState) { cardColor = Colors.deepPurple; } else { cardColor = Theme.of(context).cardColor; } } return Card( color: cardColor, child: ListTile( selected: false, leading: new Icon(Icons.lightbulb_outline), title: new Text("${model.name}"), onTap: () { // to control accessory value // get status value at first bobaos.getStatusValue( model.id, "state", (bool err, Object payload) { if (err) { return print('error ocurred $payload'); } if (payload is Map) { dynamic currentValue = payload['status']['value']; bool newValue; if (currentValue is bool) { // invert newValue = !currentValue; } else { newValue = false; } // then send new value bobaos.controlAccessoryValue( model.id, {"state": newValue}, (bool err, Object payload) { if (err) { return print('error ocurred $payload'); } }); } }); }, onLongPress: () { // TODO: dialog with additional funcs }, )); })); } 

بالنسبة لملحق switch النوع ، يتم إنشاء عنصر في القائمة العامة للملحقات ، عند التفاعل مع (onTap) يتم إرسال طلب للحصول على القيمة الحالية ، ثم لتبديل هذه القيمة. يسمح ScopedModel بإعادة رسم عنصر واجهة المستخدم للحصول على تحديثات الحالة الواردة.


لم يتم تطبيق معالج النقر الطويل لهذا الملحق.


بالنسبة لمشغل الراديو ، يبدو كما يلي:


  onLongPress: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => AccRadioPlayerControl( info: info, bobaos: bobaos, ))); }, 

يتم AccRadioPlayerControl صفحة AccRadioPlayerControl ، والتي تستخدم أيضًا ScopedModel لإدارة الحالة.


في هذا ، تم استنفاد الوصف الكامل لخوارزمية تشغيل البرنامج. لا توجد ميزات إضافية ، مثل تذكر آخر مضيف ، لا يتم تطبيق فرز الملحقات في فئات / غرف. في الوقت الحالي ، كل شيء بسيط.


المشاكل


سوف أصف المشكلة الرئيسية التي هي الآن. ما زلت لا أفهم كيفية اكتشاف اتصال WebSocket مقطوع.


يمكنني استخدام: فئة WebSocket .


عندما يكون التطبيق / الجهاز في وضع السكون لفترة طويلة ، يتم قطع الاتصال. يجب عليك العودة إلى الصفحة الأولى وإعادة فتح الخدمة المكتشفة.


خاتمة


من ناحية ، يكون Flutter سريعًا جدًا في التعلم والتطوير. تحولت ScopedModel بالنسبة لي ليكون التكرار أكثر قابلية للفهم.
تبين أن السهام مشابه لجافا سكريبت المألوف. الكتابة + أنواع ديناميكية ستتيح للجميع الكتابة بطريقة مريحة.


صعوبات في كتابة التعليمات البرمجية: تداخل كبير من الحاجيات. رد الاتصال الجحيم المعروفة بعد رفرفة تبدو مختلفة. وضع VIM و % سيكون مفيدا.


الآن بعض الأفكار حول إنترنت الأشياء. في الآونة الأخيرة ، المزيد والمزيد من الأجهزة / الخدمات الذكية التي تتطلب التسجيل في السحابة. المنافذ الصينية ، التي تحتاج إلى تثبيت التطبيق لاستخدامها ، قم بإنشاء حساب ، وبعد ذلك فقط يمكنك استخدامه.


مساعدين صوت. أليس من Yandex تتطلب سحابة لها التي يتم إرسال النص المعترف بها. تعمل أمازون أليكسا بطريقة مماثلة.


الأكثر نجاحًا ، في رأيي ، تم تصنيعه بواسطة Apple HomeKit بالتعاون مع Siri. تستخدم السحابة للتعرف على النص. التفاعل مع الأجهزة - في الشبكة المحلية.


رأيي هو أن السحابة يجب أن تكون موجودة لغرضها: التحكم عن بعد ، التحديث ، إلخ ... إذا كان يمكن التحكم في الجهاز على شبكة محلية ، فأنت بحاجة إلى القيام بذلك.


المراجع


  1. مستودع التطبيق
  2. وثائق Bobaoskit - توضح كيفية تثبيت bobaoskit.worker وتشغيل ملحق radio player .

Source: https://habr.com/ru/post/ar440088/


All Articles