
لا يوجد حد الكمال. يبدو أن كل شيء يعمل بشكل جيد ، تم إصلاح الأخطاء الطفيفة وهلم جرا.
الآن سوف أخبركم أولاً بالمشاكل التي واجهتها طوال الوقت الذي مضى منذ المقالة السابقة ، وثانياً عن الحلول التي ساهمت في الوضع الحالي للمشروع.
→ مقالة عن الإصدار السابق
التعيينات
bobaos
- وحدة npm للتفاعل مع BAOS 83x باستخدام UART. إرجاع البيانات الخام. المستخدمة في جميع الوحدات الأخرى المدرجة أدناه.
bdsd.sock
- برنامج نصي للعمل مع كائنات KNX. يخزن قائمة نقاط البيانات ، ويحول القيم عند إرسال / تلقي. من DPT1 إلى صواب / خطأ ، من DPT9 إلى الطفو. يستمع أيضًا إلى Unix Socket لتلقي الطلبات من العمليات الأخرى.
bobaos.pub
هو إصدار جديد يستخدم redis
للاتصال interprocess.
KNX /
- كائن اتصال لوحدة BAOS 83x التي تم تكوينها في ETS والتي تتوافق مع (أو لا) عنوان المجموعة (أو لا). في الإصدارات الحالية من الحديد ، الحد الأقصى للمبلغ هو 1000.
التحدي
المهمة الرئيسية هي نفس النسخة السابقة. يوجد اتصال واحد فقط بالمنفذ التسلسلي. هناك الكثير من البرامج النصية التي تعمل مع KNX. بالإضافة إلى ذلك ، أردت تنفيذ اتصال interprocess. أي بحيث لا bdsd.sock
عملية bdsd.sock
واحدة فقط إلى مأخذ التوصيل ، ولكن كل برنامج نصي قيد التشغيل يمكنه إرسال وتلقي الطلبات.
فكرة
وُلدت فكرة في رأسي لجعل وسيط الرسائل الخاص بي على node.js أعلى مآخذ يونكس ، والتي يتصل بها العملاء ، والمشاركة في الموضوعات وتلقي / إرسال الرسائل وفقًا للرمز الموصوف فيهم. كنت أعرف أن هناك حلول جاهزة بالفعل ، والتي لم يدرسها سوى الكسل البطيء ، لكن فكرة اتخاذ قراري كانت مهووسة.
ونتيجة لذلك ، يتم إطلاق الخدمة.
كتب المسجل الذي يرسل رسائل إلى الموضوع. يتلقى المشتركون ويحق لهم فعل أي شيء ، أو بالأحرى ، ما يوصف. مريحة - يمكن الاطلاع على سجلات من عدة مصادر في إخراج وحدة تحكم واحدة.
كتبت ونشرت حزمة bobaos.pub في npm ، والتي ، على عكس bdsd.sock ، لم تعد تنشئ ملف مأخذ توصيل ، ولكنها تتصل بوسيط. للوهلة الأولى ، كل شيء يعمل كما ينبغي.
المشكلة
ثم قمت بتشغيل برنامج نصي يرسل بشكل دوري الطلبات إلى ناقل KNX بين عشية وضحاها. استيقظت في الصباح ، من خلال وميض المصابيح التي تشير إلى إرسال / نقل البيانات ، أدركت أن هناك شيئًا ما خطأ. الرسائل لم تصل في الوقت المحدد. لقد وجدت أن وسيط الرسائل المكتوب ذاتيًا تناول تقريبا جميع ذاكرة الوصول العشوائي (RAM) 512 ميجابايت المتوفرة (من BeagleBoard Black). أكد مزيد من العمل مع nodejs أن الذاكرة هي نقطة ضعف البرامج النصية js.
الحل
ونتيجة لذلك ، تقرر التبديل من مآخذ يونكس العامة إلى ريديس (بالمناسبة ، يعرف أيضًا كيفية العمل معهم). ربما كان الأمر يستحق فرز الذاكرة ، والعثور على التسريبات ، لكنني أردت الجري بشكل أسرع.
bobaos يعني اتصال UART مع التفاف الرسالة في FT1.2 ، لدينا اتصال متزامن. أي <..--..>
. تقوم وحدة bobaos ، المسؤولة عن الاتصال ، بتخزين جميع الطلبات في المصفوفة ، وتسحبها بدورها ، وترسلها إلى UART ، ومع الرد الوارد ، تحل الوعد المسؤول عن هذا الطلب.
يمكنك السير بهذه الطريقة: تستمع الخدمة إلى قناة PUB / SUB redis ، وتقبل الطلبات ، وترسلها إلى KNX. في هذه الحالة ، يقع التحميل على قائمة انتظار الطلب على وحدة js bobaos
. للتنفيذ ، تحتاج إلى كتابة وحدة نمطية بسيطة مشتركة في قناة وتحويل الرسائل باستخدام طريقة JSON.parse()
. كذلك هذه الوحدة يمكن استخدامها في البرامج النصية الأخرى.
خيار آخر انتهى بي الأمر إلى redis
: استخدام مدير redis
موجود أعلى redis
. هناك العديد من الخيارات التي تم إجراؤها على bee-queue
.
→ تحت غطاء محرك السيارة
يصف كيف يعمل bee-queue
. إذا قمت بتطبيق هذه المكتبة للغات البرمجة الأخرى ، فيمكنك إنشاء مكتبات عميل bobaos
بهذه الطريقة.
في الإصدار الثاني ، يتم تخزين جميع الطلبات في قوائم redis
، redis
سحبها بدوره وإرسالها إلى المنفذ التسلسلي.
علاوة على ذلك ، تتبع إعادة كتابة الإصدار السابق ، لكنني قمت بالفعل بتخزين جميع البيانات على نقاط البيانات في قاعدة بيانات redis
. الإزعاج الوحيد الذي أواجهه هو أن جميع الطلبات غير متزامنة ، وبالتالي فإن الحصول على مجموعة من القيم أصعب قليلاً من مجرد الوصول إلى المصفوفة.
تم إجراء تحسينات بسيطة.
إذا كانت هناك طرق منفصلة getValue/getValues/readValue/readValues/setValue/setValues
، الآن getValue/readValue/setValues
يقبل كلاً من قيمة واحدة وصفيف.
getValue([id1, id2, ...])
طريقة getValue([id1, id2, ...])
في الإصدار السابق طلبًا إلى المنفذ التسلسلي لكل نقطة بيانات. ولكن هناك فرصة لإرسال طلب لعدة قيم. القيود - يجب أن يكون حجم الاستجابة مساويًا لـ BufferSize
، الحد الأقصى - 250 بايت ؛ من المستحيل أيضًا تجاوز عدد الكائنات ؛ للإصدارات الحالية من وحدات BAOS 83x - 1000.
أطوال القيم معروفة ، الرأس أيضًا. علاوة على ذلك ، خوارزمية بسيطة إلى حد ما مع حين وتنتظر دورات =)
- فرز مجموعة ، وحذف العناصر المكررة ، إن وجدت. نحصل على مجموعة
idUniq
. - نبدأ الدورة
i < idUniq.length
، حيث نقوم بما يلي:
أ) start: idUniq[i]
، لذلك نحن نعتبر الحد الأقصى لعدد القيم التي يمكننا الحصول عليها. على سبيل المثال ، إذا كانت جميع الكائنات من النوع DPT1 / DPT5 (1 بايت) ، فيمكننا الحصول على قيم 48. هناك ملاحظة واحدة: إذا ، على سبيل المثال ، قمنا بتكوين الكائنات #[1, 2, 3, 10, 20]
، ثم عند الاستعلام عن GetDatapointValue.Req(1, 30)
، ستُرجع الاستجابة صفر قيم أحادية البايت حتى بالنسبة لنقاط البيانات غير الموجودة [4, 5, 6, ..., 30]
.
ب) يتم العد في دورة جديدة j < i + max
(حيث يبلغ max
50 ، أو إذا كان قريبًا من 1000 ، ثم 1000 - id + 1
بحد أقصى 1000 - id + 1
، فسيكون 11 بالنسبة لـ 990 ، 11 ، من 999 - 2) ، إذا التقينا أثناء الحساب عناصر الصفيف من الاستعلام الأصلي ، ثم قم بتعيين الفهرس i
المتغير i
.
ج) إذا تجاوز الطول المحسوب في الدورة j
الحد الأقصى لطول المخزن المؤقت ، idUniq
عنصر بطاقة الاستعلام {start: start, number: number}
، أو idUniq
في صفيف منفصل ، أو زيادة المتغير i
(أو تعيين الفهرس إلى idUniq
الموجود في الصفيف) ، المقاطعة دورة j
، يتم إعادة تشغيل كلتا الدورتين.
وبالتالي ، نحن تشكيل عدة طلبات bobaos
. على سبيل المثال ، إذا قمت بإرسال طلب getValue([1, 2, 3, 40, 48, 49, 50, 100, 998, 999, 1000])
، فإن الطلبات يمكن أن تكون على النحو التالي لحالة خاصة:
{start: 1, number: 48}, // 1, 2, 3, 40, 48 {start: 49, number: 48}, // 49, 50 {start: 100, number: 48}, // 100 {start: 998, number: 3} // 998, 999, 1000
يمكن القيام به بشكل مختلف:
{start: 1, number: 48}, // 1, 2, 3, 40, 48 {start: 49, number: 2}, // 49, 50 {start: 100, number: 1}, // 100 {start: 998, number: 3} // 998, 999, 1000
سيكون هناك العديد من الطلبات ، وأقل البيانات. لكنني توقفت عند الخيار الأول ، حيث يتم تخزين القيم التي تم الحصول عليها في قاعدة بيانات redis
، على التوالي ، يمكن الحصول عليها باستخدام طريقة getStoredValue
، التي أحاول استخدامها أكثر من getValue
، التي ترسل البيانات عبر المنفذ التسلسلي.
يتم إنشاء قائمة انتظار منفصلة لأساليب خدمة ping/get sdk state/reset
. وبالتالي ، إذا كان هناك خطأ ما في الاتصال على المنفذ التسلسلي (عداد الضلال في الإطار ، وما إلى ذلك) وتوقف التنفيذ في بعض المهام ، فيمكنك إرسال طلب reset
في قائمة انتظار أخرى ، وبالتالي ، sdk
تشغيل sdk
.
جانب العميل - bobaos.sub
للتحكم في نقاط بيانات KNX في البرامج النصية للمستخدم ، يمكن استخدام وحدة bobaos.sub
.
يغطي المثال التالي جميع وظائف الوحدة النمطية:
const BobaosSub = require("bobaos.sub"); // : // redis: url // request_channel: "bobaos_req" , // service_channel: "bobaos_service" , // broadcast_channel: "bobaos_bcast" let my = BobaosSub(); my.on("connect", _ => { console.log("connected to ipc, still not subscribed to channels"); }); my.on("ready", async _ => { try { console.log("hello, friend"); console.log("ping:", await my.ping()); console.log("get sdk state:", await my.getSdkState()); console.log("get value:", await my.getValue([1, 107, 106])); console.log("get stored value:", await my.getValue([1, 107, 106])); console.log("get server item:", await my.getServerItem([1, 2, 3])); console.log("set value:", await my.setValue({id: 103, value: 0})); console.log("read value:", await my.readValue([1, 103, 104, 105])); console.log("get programming mode:", await my.getProgrammingMode()); console.log("set programming mode:", await my.setProgrammingMode(true)); console.log("get parameter byte", await my.getParameterByte([1, 2, 3, 4])); console.log("reset", await my.reset()); } catch(e) { console.log("err", e.message); } }); my.on("datapoint value", payload => { // , payload , // Array.isArray(payload) console.log("broadcasted datapoint value: ", payload); }); my.on("server item", payload => { // , payload , // Array.isArray(payload) console.log("broadcasted server item: ", payload); }); my.on("sdk state", payload => { console.log("broadcasted sdk state: ", payload); });
تمت إعادة كتابة واجهة سطر الأوامر. حول كيفية تنفيذها المقالة التالية:
→ كتابة CLI على NodeJS
أصبحت الفرق أقصر وأكثر وضوحا وأكثر عملية.
bobaos> progmode ? BAOS module in programming mode: false bobaos> progmode 1 BAOS module in programming mode: true bobaos> progmode false BAOS module in programming mode: false bobaos> description 1 2 3 #1: length = 2, dpt = dpt9, prio: low, flags: [C-WTU] #2: length = 1, dpt = dpt1, prio: low, flags: [C-WT-] #3: length = 1, dpt = dpt1, prio: low, flags: [C-WT-] bobaos> set 2: 0 20:27:06:239, id: 2, value: false, raw: [AA==] bobaos> set [2: 0, 3: false] 20:28:48:586, id: 2, value: false, raw: [AA==] 20:28:48:592, id: 3, value: false, raw: [AA==]
خاتمة
وكانت النتيجة نظام عمل مستقر. Redis
كخلفية يعمل بشكل جيد ثابت. خلال التنمية ، كانت معبأة الكثير من المخاريط. لكن عملية التعلم هي في بعض الأحيان أنه أمر لا مفر منه. من تجربتي ، ألاحظ أن عمليات nodejs
تستهلك الكثير من ذاكرة الوصول العشوائي (20 ميغابايت في البداية) وقد تكون هناك تسربات. بالنسبة للأتمتة المنزلية ، يعد هذا أمرًا بالغ الأهمية - لأن البرنامج النصي يجب أن يعمل باستمرار ، وإذا كان ينمو أكثر وأكثر بمرور الوقت ، فإنه عند نقطة معينة يمكن أن يستغرق كل المساحة. لذلك ، يجب عليك كتابة البرامج النصية بعناية ، وفهم كيفية عمل جامع البيانات المهملة ، والحفاظ على كل شيء تحت السيطرة.
تحديث الوثائق يمكن العثور عليها هنا .
في المقالة التالية ، سأتحدث عن كيفية استخدام خدمة redis
و bee-queue
redis
لملحقات البرامج.
محدث: bobaoskit - الملحقات ، dnssd و WebSocket
سأكون سعيدا بأي ملاحظات.