ส่วนประกอบหน้าเว็บ SVG สำหรับ IoT และผู้สร้าง (ตอนที่ 2)

เผยแพร่แล้ว: 2022-03-10
สรุปด่วน ↬ เมื่อออกแบบอินเทอร์เฟซสำหรับหน้าเว็บ IoT เรามีตัวเลือกมากมายเสมอ ในส่วนก่อนหน้าของบทความนี้ Richard Leddy ให้ความกระจ่างเกี่ยวกับความหมายของ IoT และวิธีการใช้ Vue.js เพื่อโฮสต์กลุ่มของส่วนต่อประสานระหว่างมนุษย์กับเครื่องจักร IoT วันนี้ มาดูพาเนลการโหลดแบบสันหลังยาวและวิธีทำให้สถานะ Vue ซิงค์กับอุปกรณ์กันดีกว่า

ดังนั้นเราจึงมีวิธีโหลดเมนูของไอคอน SVG แบบไดนามิกซึ่งสร้างขึ้นเพื่อตอบสนองโดยการโหลดพาเนลหากต้องการ แต่ไอคอนไม่ใช่ส่วนประกอบจริง เราสามารถใช้เคล็ดลับง่ายๆ ในการนำเข้า SVG สำหรับแต่ละไอคอนและส่งไปยังแอปพลิเคชัน Vue ง่ายพอที่จะสร้างรายการไอคอน และแต่ละไอคอนมีปฏิกิริยาในลักษณะเดียวกัน ยกเว้นความแตกต่างของข้อมูลเพียงเล็กน้อย ความแตกต่างของข้อมูลทำให้สามารถผูกชื่อของพาเนลกับแต่ละไอคอนในลักษณะที่ตัวจัดการสำหรับการคลิกปุ่มของไอคอนสามารถส่งต่อได้

เมื่อโหลดพาเนลในรูปแบบของคอมโพเนนต์ Vue ทุกอย่างเกี่ยวกับพาเนลและคอมโพเนนต์จะต้องโหลด เทมเพลต JavaScript และอื่นๆ ดังนั้น งานในการจัดการการโหลดแผงควบคุมจึงใหญ่กว่าที่เราเคยพบในการสนทนานี้

ลองดูวิธีการของ Vue ในการจัดเตรียม hook สำหรับการโหลดแบบอะซิงโครนัส ตัวอย่างต่อไปนี้มาจากคู่มือ Vue

 Vue.component('async-example', function (resolve, reject) { setTimeout(function () { // Pass the component definition to the resolve callback resolve({ template: '<div>I am async!</div>' }) }, 1000) })

คู่มือนี้บอกเราว่าฟังก์ชัน setTimeout เป็นตัวอย่างของวิธีใช้การซิงโครไนซ์กับส่วนประกอบ Vue สังเกตว่าเมื่อก่อนเคยมีอ็อบเจ็กต์เป็นพารามิเตอร์ตัวที่สองของ Vue.component ตอนนี้มีฟังก์ชันซึ่งเรียกว่าฟังก์ชันของโรงงาน ภายในการเรียกกลับแบบ resolve คือนิยามคอมโพเนนต์ ซึ่งเคยเป็นพารามิเตอร์ตัวที่สองของ Vue.component มาก่อน

เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

ดังนั้นฉันจึงต้องดูตัวอย่างนี้สักครู่ก่อนที่จะเข้าใจฉัน นี่เป็นอีกตัวอย่างหนึ่งที่เหมาะกับฉันมากกว่า:

 Vue.component('async-example', function (resolve, reject) { // Vue will call this function and promise itself to handle // it when it gets back with data. // this function can then call a promising object loader // here the 'loader' function is some abstract function. // Most likely the application will use 'fetch' // but it could be something else. loader('/my/resource/on/server.json'). then(function (JSON_data) { var object = transformJSONToJSObject(JSON_data); resolve(object) }).catch( (error) => { handle it } );

ดูเหมือนว่าสิ่งที่ถูกต้องที่จะทำเพื่อให้ฟังก์ชันทั่วๆ ไปใช้กับแบบฟอร์มนี้

 function componentLoader(c_name,resource_url) { Vue.component(c_name, function (resolve, reject) { loader(resource_url). then(function (JSON_data) { var object = transformJSONToJSObject(JSON_data); resolve(object) }).catch( (error) => { handle it } ); }

โดยทั่วไป ในการโหลดส่วนประกอบ เราจำเป็นต้องมีบรรทัดดังต่อไปนี้:

 componentLoader('ThermoPanel','./JSON/thermo-panel.json');

ตอนนี้ JSON ที่กำลังโหลดอยู่คืออะไร? สามารถรวมทุกอย่างเกี่ยวกับส่วนประกอบได้ ในกรณีนี้ อาจรวมถึงเทอร์โมมิเตอร์ สวิตช์เครื่อง สไลเดอร์ เกจ และอื่นๆ ในฐานะส่วนประกอบแผง แม้ว่าจะดูดีกว่าที่จะเก็บส่วนประกอบต่างๆ ไว้บนหน้าเว็บ แต่จริงๆ แล้วอาจใช้ฟิลด์ส่วนประกอบย่อยที่อยู่ในตัวอย่างที่ยาวกว่าสำหรับ 'แผงระบายความร้อน' ที่เราทำก่อนหน้านี้และสำหรับแผงอื่นๆ ที่สร้างในลักษณะเดียวกันได้ดีกว่า JSON จะมีโครงสร้างพาเนลที่สมบูรณ์

อย่างไรก็ตาม หากผู้อ่านสังเกตเห็นการรวมของการเรียกใช้ฟังก์ชันเพื่อ transformJSONToJSObject เขาจะเข้าใจว่า JSON อาจถูกเข้ารหัสด้วยวิธีใดวิธีหนึ่งเพื่อทำให้การขนส่งง่ายขึ้นและทำให้เซิร์ฟเวอร์จัดการกับคำจำกัดความได้ง่ายขึ้น ท้ายที่สุด คำจำกัดความจะรวมเทมเพลต SVG ที่สมบูรณ์ คำจำกัดความของฟังก์ชัน และนิพจน์ JavaScript อื่นๆ นอกจากนี้ ออบเจ็กต์ JSON อาจมีมากกว่าคำจำกัดความแผง เนื่องจากข้อมูลบางอย่างอาจช่วยในการทำบัญชีหรือการตรวจสอบ ดังนั้น เราสามารถคาดหวังว่าจะได้รับการปฏิบัติต่อวัตถุบางอย่างเมื่อได้รับ

สำหรับการเข้ารหัส ข้อมูลที่มาจากเซิร์ฟเวอร์อาจถูกเข้ารหัสได้หลายวิธี บางทีมันอาจจะเป็นเพียงการเข้ารหัส URL หรือปลอดภัยกว่านั้นก็อาจถูกเข้ารหัส สำหรับการสนทนานี้ เราสามารถใช้การเข้ารหัส URL ได้

เครื่องมือบางอย่างที่พร้อมใช้งานสำหรับการสร้างแอปพลิเคชัน Vue จะดูแลการแปลง JSON อย่างไม่ต้องสงสัย แต่การสนทนานี้ยังหลีกเลี่ยงการใช้เครื่องมือบรรทัดคำสั่ง การละเว้นนี้ไม่ได้แย่ขนาดนั้น เนื่องจากเราใช้ Vue กับทรัพยากรขั้นต่ำ โดยใช้แท็กสคริปต์เพียงแท็กเดียวสำหรับการอ้างอิงถึง CDN อย่างไรก็ตาม ฉันขอแนะนำให้ดูเครื่องมือบรรทัดคำสั่งโดยเฉพาะสำหรับการจัดระเบียบโครงการ

เมื่อ JSON มาถึงหน้า เนื่องจากส่วนประกอบถูกประกอบเข้ากับส่วนประกอบย่อยอย่างสมบูรณ์ จึงไม่ต้องดำเนินการใดๆ เพื่อดึงข้อมูลชิ้นส่วนอีกต่อไป เราสามารถตั้งสมมติฐานได้ว่าองค์ประกอบทั้งหมดจะถูกกำหนดอย่างครบถ้วนสำหรับส่วนที่เหลือของการสนทนานี้ แต่การประกอบลำดับชั้นของส่วนประกอบที่สมบูรณ์จะต้องใช้เครื่องมือบรรทัดคำสั่งในบางจุด

กระบวนการแก้ไข SVG จะต้องดำเนินการบางอย่างด้วย กระบวนการแก้ไข SVG ช่วยให้ผู้ออกแบบสามารถวาดแผงและส่วนประกอบทั้งหมดบนแผงได้ แต่, แต่ละองค์ประกอบย่อยจะต้องมีการระบุ, เรียกออกมาเป็นกลุ่ม, หรือกำหนดตำแหน่ง. การใช้ภาพวาดต้องใช้วิธีการใด ๆ กับ SVG เพื่อให้แท็กองค์ประกอบ Vue สามารถแทนที่กลุ่มหรือองค์ประกอบกราฟิก ด้วยวิธีนี้ การเรนเดอร์ศิลปินใดๆ สามารถกลายเป็นเทมเพลตได้ และจะต้องแยกส่วนประกอบย่อยที่วาดออกมาเป็นเทมเพลตสำหรับส่วนประกอบย่อยของ Vue

Parsimony ประเภทนี้ตรงกันข้ามกับเวิร์กโฟลว์ของเฟรมเวิร์ก JavaScript ส่วนใหญ่ เฟรมเวิร์กเกี่ยวกับการรวบรวมเพจ แต่การตัดต่อหรือการวาดภาพ ทำให้เกิดบางสิ่งที่ศิลปินประกอบขึ้นแล้ว ในทางปฏิบัติ ผลลัพธ์ของการแก้ไขไม่ได้จัดเตรียมไฟล์ข้อความที่สอดคล้องโดยตรงกับข้อกำหนดของคอมโพเนนต์เฟรมเวิร์ก

เพิ่มเติมเกี่ยวกับกระบวนการแก้ไขอาจมีการพิจารณาในการสนทนาอื่น มีอะไรมากมาย แต่สำหรับตอนนี้ เรามีเครื่องมือที่เราต้องการเพื่อโหลดส่วนประกอบตามลำดับชั้นและทำให้มันมีชีวิต

แอปพลิเคชั่นขี้เกียจ

สำหรับการสร้างแผง IoT เรามีแถบการเลือกที่ตอบสนองต่อการค้นหาอยู่แล้ว และเรามีวิธีโหลดส่วนประกอบเมื่อเราต้องการ เราแค่ต้องเชื่อมต่อส่วนเหล่านี้เข้าด้วยกัน และสุดท้าย เราต้องตรวจสอบให้แน่ใจว่าแผงต่างๆ ปรากฏขึ้นและแผงนั้นเริ่มทำงานเมื่อแผงนั้นปรากฏขึ้น

การโหลดพาเนลแบบสันหลังโดยโค้ด async ด้านบนเป็นภาพร่างของแนวคิด แต่โชคดีที่บางคนได้ทดลองหาวิธีเพื่อให้แน่ใจว่าส่วนประกอบทุกประเภทสามารถโหลดได้ มีรายการรหัสหนึ่งรายการที่แสดงวิธีอัปเดตแอป Vue ด้วยองค์ประกอบใหม่ประเภทต่างๆ นั่นคือกลไกที่จำเป็นสำหรับการอัปเดตส่วนที่กำหนดของหน้าด้วยแผงประเภทต่างๆ

ด้วยความสามารถในการเพิ่มพาเนลประเภทต่างๆ และด้วยกลไกง่ายๆ ในการโหลดคำจำกัดความ เราจึงมีหน้าการค้นหาแผงของเราในที่สุด

นี่คือ HTML ที่เราต้องการในหน้าของเรา เพื่อให้แอป Vue สามารถวางส่วนประกอบในไดนามิก:

 <template v-for="(panel, index) in panelList"> <component :is="panel" :key="panel.name"></component> </template>

แท็ก component คือเมตาแท็ก Vue ดูข้อมูลอ้างอิงสำหรับส่วนประกอบไดนามิก คุณสมบัติ คุณลักษณะพิเศษ ที่ใช้สำหรับแท็ก component ในกรณีนี้คือคีย์และ is แอตทริบิวต์สำหรับส่วนประกอบแบบไดนามิก และ key ทำให้แน่ใจได้ว่าเด็กใหม่จะมีอัตลักษณ์ที่แตกต่างกันและช่วยให้ Vue ตัดสินใจว่าจะวาดอะไร

“ลูกของผู้ปกครองเดียวกันต้องมีคีย์ที่ไม่ซ้ำกัน คีย์ที่ซ้ำกันจะทำให้เกิดข้อผิดพลาดในการแสดงผล”

แท็ก template จะวนซ้ำส่วนประกอบที่มีให้ในฟิลด์ข้อมูล panelList ของแอปพลิเคชัน

ดังนั้น เริ่มต้นด้วยคำจำกัดความ Vue ระดับแอปพลิเคชันสำหรับแอปไอคอน เราสามารถทำการเปลี่ยนแปลงเพื่อรวม panelList ไว้ในองค์ประกอบข้อมูล (ตอนนี้เรียกมันว่า panelApp)

 var panelApp = new Vue({ el: '#PanelApp', data: { iconList: [ // Where is the data? Still on the server. ], panelList: [ ], queryToken : "Thermo Batches" // picked a name for demo }, methods : { goGetPanel: function (pname) { // var url = panelURL(pname); // this is custom to the site. fetch(url).then((response) => { // this is now browser native response.text().then((text) => { var newData = decodeURIComponent(text); eval(pHat); // widgdef = object def, must be assignment pHat = widgdef; var pnameHat = pname + pcount++; pHat.name = pnameHat; // this is needed for the key this.panelList.push(pHat); // now it's there. }).catch( error => { /* handle it */ }); } } });

นอกจากการเพิ่มในพาเนลแล้ว goGetPanel ยังอยู่ในรูปแบบที่จำเป็นสำหรับการรับข้อกำหนดส่วนประกอบจากฐานข้อมูลหรือร้านค้าอื่นๆ ฝั่งเซิร์ฟเวอร์ต้องระมัดระวังในการส่งโค้ด JavaScript ในรูปแบบที่ถูกต้อง สำหรับสิ่งที่ดูเหมือนวัตถุที่มาจากเซิร์ฟเวอร์เราได้เห็นแล้ว เป็นชนิดของอ็อบเจ็กต์ที่ใช้เป็นพารามิเตอร์ Vue.component

นี่คือเนื้อหาที่สมบูรณ์ของแอป Vue ที่มีเมนูเป็นผลการค้นหาและเป็นที่สำหรับวางพาเนลที่ดึงมาจากเซิร์ฟเวอร์เมื่อผู้ใช้คลิกที่ไอคอน

 <div> <!-- Recognize the name from the Vue doc --> <div> <h2 itemprop="name">Request MCU Groups</h2> <p itemprop="description">These are groups satistfying this query: {{queryToken}}.</p> <button>Find All</button> <button>Find 5 Point</button> <button>Find 6 Point</button> </div> <!-- Here is a Vue loop for generating a lit --> <div class="entryart"> <button v-for="iconEntry in iconList" @click="goGetPanel(iconEntry.name)" > <div v-html="iconEntry.icon"> </div> </button> </div> <div class="entryart" > <template v-for="(panel, index) in panelList"> <component :is="panel" :key="panel.name" :ref="panel.name" ></component> </template> </div> </div>

ใน div ที่แล้ว แท็ก component มีพารามิเตอร์ ref ที่ผูกไว้กับชื่อพาเนล พารามิเตอร์อ้างอิงช่วยให้แอป Vue สามารถระบุส่วนประกอบที่จะอัปเดตด้วยข้อมูลและแยกส่วนประกอบออกจากกัน พารามิเตอร์ ref ยังอนุญาตให้แอปพลิเคชันของเราเข้าถึงส่วนประกอบที่โหลดแบบไดนามิกใหม่

ในแอปพาเนลเวอร์ชันทดสอบหนึ่ง ฉันมีตัวจัดการช่วงเวลาต่อไปนี้:

 setInterval(() => { var refall = panelApp.$refs; // all named children that panels for ( var pname in refall ) { // in an object var pdata = refall[pname][0]; // off Vue translation, but it's there. pdata.temp1 = Math.round(Math.random()*100); // make thermos jump around. pdata.temp2 = Math.round(Math.random()*100); } },2000)

รหัสแสดงแอนิเมชั่นเล็กน้อยโดยเปลี่ยนเทอร์โมมิเตอร์แบบสุ่ม แต่ละแผงมีเทอร์โมมิเตอร์สองตัว และแอปนี้อนุญาตให้ผู้ใช้เพิ่มแผงต่อไปได้ (ในเวอร์ชันสุดท้าย แผงบางอันต้องทิ้งไป) ผู้อ้างอิงกำลังถูกเข้าถึงโดยใช้ panelApp.$refs ซึ่งเป็นฟิลด์ที่ Vue สร้างขึ้นโดยให้ข้อมูลผู้ refs ในแท็ก component

นี่คือลักษณะของเทอร์โมมิเตอร์แบบกระโดดแบบสุ่มในภาพรวมเดียว:

คอลเล็กชันแผงภาพเคลื่อนไหวสำหรับแผงประเภทหนึ่ง (หรือส่วนประกอบ) ที่แสดงเทอร์โมมิเตอร์
คอลเล็กชันแผงภาพเคลื่อนไหวสำหรับแผง (หรือส่วนประกอบ) ประเภทเดียว (ตัวอย่างขนาดใหญ่)

การเชื่อมต่อแผงควบคุมเข้ากับอุปกรณ์ IoT

ดังนั้นโค้ดสุดท้ายคือการทดสอบ setInterval ที่อัปเดตเทอร์โมมิเตอร์ด้วยค่าสุ่มทุกๆ สองวินาที แต่สิ่งที่เราต้องการจะทำคืออ่านข้อมูลจริงจากเครื่องจริง เพื่อที่จะทำอย่างนั้น เราจะต้องมีรูปแบบการสื่อสารบางอย่าง

มีหลากหลายวิธี แต่ลองใช้ MQTT ซึ่งเป็นระบบข้อความแบบผับ/ย่อย SPWA ของเราสามารถสมัครรับข้อความจากอุปกรณ์ได้ตลอดเวลา เมื่อได้รับข้อความเหล่านั้น SPWA สามารถนำแต่ละข้อความไปยังตัวจัดการข้อมูลที่เหมาะสมสำหรับแผงที่แมปไปยังอุปกรณ์ที่ระบุในข้อความ

ดังนั้น โดยพื้นฐานแล้ว สิ่งที่เราต้องทำคือแทนที่ setInterval ด้วยตัวจัดการการตอบสนอง และนั่นจะเป็นแผงเดียว เราอาจต้องการแมปพาเนลกับตัวจัดการขณะโหลด และก็ขึ้นอยู่กับเว็บเซิร์ฟเวอร์ที่จะเห็นว่ามีการส่งการแมปที่ถูกต้อง

เมื่อเว็บเซิร์ฟเวอร์และ SPWA มีเพจพร้อมสำหรับการดำเนินการแล้ว เว็บเซิร์ฟเวอร์ก็ไม่จำเป็นต้องดูแลการส่งข้อความระหว่างเพจและอุปกรณ์อีกต่อไป โปรโตคอล MQTT ระบุเซิร์ฟเวอร์การเราต์เพื่อจัดการ pub/sub มีการสร้างเซิร์ฟเวอร์ MQTT จำนวนหนึ่ง บางส่วนของพวกเขาเป็นโอเพ่นซอร์ส หนึ่งที่นิยมมากคือ Mosquito และมีการพัฒนาบางส่วนที่ด้านบนของ Node.js

ขั้นตอนสำหรับหน้านั้นง่าย SPWA สมัครรับข้อมูลจากหัวข้อ หัวข้อเวอร์ชันหนึ่งที่ดีคือตัวระบุสำหรับ MCU เช่น ที่อยู่ MAC หรือหมายเลขซีเรียล หรือ SPWA สามารถสมัครรับการอ่านค่าอุณหภูมิทั้งหมดได้ แต่แล้วหน้าเพจก็จะต้องทำการกรองข้อความจากอุปกรณ์ทั้งหมด การเผยแพร่ใน MQTT นั้นเป็นการเผยแพร่หรือมัลติคาสต์เป็นหลัก

มาดูกันว่า SPWA จะเชื่อมต่อกับ MQTT อย่างไร

กำลังเริ่มต้น MQTT บน SPWA

มีไลบรารีไคลเอนต์หลายตัวให้เลือก ตัวอย่างหนึ่งคือ MQTT.js อีกอย่างคือสุริยุปราคาปะโฮ มีมากขึ้นแน่นอน ลองใช้ Eclipse Paho เพราะมันมีเวอร์ชันที่จัดเก็บ CDN เราเพียงแค่ต้องเพิ่มบรรทัดต่อไปนี้ในหน้าของเรา:

 <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>

ไคลเอ็นต์ MQTT ต้องเชื่อมต่อกับเซิร์ฟเวอร์ก่อนจึงจะสามารถส่งและรับข้อความได้ ดังนั้น ต้องรวมบรรทัดการตั้งค่าการเชื่อมต่อไว้ใน JavaScript ด้วย เราสามารถเพิ่มฟังก์ชัน MQTTinitialize ซึ่งตั้งค่าไคลเอนต์และการตอบสนองสำหรับการจัดการการเชื่อมต่อและการรับข้อความ

 var messagesReady = false; var mqttClient = null; function MQTTinitialize() { mqttClient = new Paho.MQTT.Client(MQTTHostname, Number(MQTTPort), "clientId"); mqttClient.onMessageArrived = onMessageArrived; // connect the client mqttClient.connect({ onSuccess: () => { messagesReady = true; } }); // set callback handlers mqttClient.onConnectionLost = (response) => { // messagesReady = false; // if (response.errorCode !== 0) { console.log("onConnectionLost:"+response.errorMessage); } setTimeout(() => { MQTTinitialize() },1000); // try again in a second }; }

การตั้งค่าการสมัครสมาชิก

เมื่อพร้อมเชื่อมต่อแล้ว ลูกค้าสามารถสมัครรับข้อมูลจากช่องข้อความ ส่งข้อความบนช่องเหล่านั้น ฯลฯ กิจวัตรเพียงไม่กี่ขั้นตอนก็สามารถทำงานส่วนใหญ่ที่จำเป็นในการเชื่อมต่อพาเนลด้วยเส้นทาง MQTT ได้

สำหรับแผง SPWA สามารถใช้ช่วงเวลาของการสมัครเพื่อสร้างการเชื่อมโยงระหว่างแผงควบคุมและหัวข้อ ตัวระบุ MCU

 function panelSubcription(topic,panel) { gTopicToPanel[topic] = panel; gPanelToTopic[panel] = topic; mqttClient.subscribe(topic); }

เนื่องจาก MCU กำลังเผยแพร่ในหัวข้อ SPWA จะได้รับข้อความ ที่นี่ข้อความ Paho ถูกคลายออก จากนั้นข้อความจะถูกส่งไปยังกลไกของแอปพลิเคชัน

 function onMessageArrived(pmessage) { // var topic = pmessage.destinationName; var message = pmessage.payloadString; // var panel = gTopicToPanel[topic]; deliverToPanel(panel,message); }

ดังนั้น ตอนนี้ สิ่งที่เราต้องทำคือสร้าง deliverToPanel ซึ่งน่าจะเหมือนกับตัวจัดการช่วงเวลาที่เราเคยมีมาก่อน อย่างไรก็ตาม มีการระบุแผงควบคุมอย่างชัดเจน และอาจอัปเดตเฉพาะข้อมูลสำคัญที่ส่งในข้อความนั้นๆ

 function deliverToPanel(panel,message) { var refall = panelApp.$refs; // all named children that panels var pdata = refall[panel][0]; // off Vue translation, but it's there. var MCU_updates = JSON.parse(message); for ( var ky in MCU_updates ) { pdata[ky] = MCU_updates[ky] } }

ฟังก์ชัน deliverToPanel นี้เป็นนามธรรมเพียงพอที่จะอนุญาตการกำหนดพาเนลที่มีจุดข้อมูลจำนวนเท่าใดก็ได้สำหรับแอนิเมชัน

การส่งข้อความ

เพื่อให้แอปพลิเคชันวนรอบระหว่าง MCU และ SPWA สมบูรณ์ เรากำหนดฟังก์ชันเพื่อส่งข้อความ

 function sendPanelMessage(panel,message) { var topic = gPanelToTopic[panel]; var pmessage = new Paho.MQTT.Message(message); pmessage.destinationName = topic; mqttClient.send(pmessage); }

ฟังก์ชัน sendPanelMessage ไม่มากกว่าการส่งข้อความบนเส้นทางหัวข้อเดียวกันกับที่ SPWA สมัครรับข้อมูล

ในขณะที่เราวางแผนที่จะสร้างปุ่มไอคอนที่รับผิดชอบในการนำพาเนลจำนวนหนึ่งสำหรับคลัสเตอร์ MCU เดียว จะมีมากกว่าหนึ่งพาเนลที่ต้องดูแล แต่เราจำไว้เสมอว่าแต่ละพาเนลสอดคล้องกับ MCU เดียว ดังนั้นเราจึงมีการทำแผนที่แบบหนึ่งต่อหนึ่ง ซึ่งเราอาจใช้แผนที่ JavaScript สองแผนที่สำหรับแผนที่และส่วนผกผัน

แล้วเราจะส่งข้อความถึงเมื่อไหร่? โดยปกติ แอปพลิเคชันแผงควบคุมจะส่งข้อความเมื่อต้องการเปลี่ยนสถานะของ MCU

การรักษาสถานะมุมมอง (Vue) ให้ตรงกันกับอุปกรณ์

สิ่งที่ยอดเยี่ยมอย่างหนึ่งเกี่ยวกับ Vue ก็คือ มันง่ายมากที่จะทำให้โมเดลข้อมูลซิงโครไนซ์กับกิจกรรมของผู้ใช้ ซึ่งอาจแก้ไขฟิลด์ คลิกที่ปุ่ม ใช้ตัวเลื่อน ฯลฯ เราสามารถมั่นใจได้ว่าการเปลี่ยนแปลงของปุ่มและฟิลด์จะ แสดงผลทันทีในช่องข้อมูลของส่วนประกอบ

แต่เราต้องการให้การเปลี่ยนแปลงเริ่มทำงานกับข้อความที่ส่งไปยัง MCU ทันทีที่การเปลี่ยนแปลงเกิดขึ้น ดังนั้นเราจึงพยายามใช้ประโยชน์จากเหตุการณ์อินเทอร์เฟซที่ Vue อาจควบคุม เราพยายามที่จะตอบสนองต่อเหตุการณ์ดังกล่าว แต่หลังจากที่โมเดลข้อมูล Vue พร้อมที่จะใช้กับค่าปัจจุบันแล้วเท่านั้น

ฉันสร้างแผงแบบอื่นขึ้นมา แผงนี้มีปุ่มที่ดูเป็นศิลปะ (อาจได้รับแรงบันดาลใจจาก Jackson Pollock) และฉันได้เปลี่ยนมันเป็นสิ่งที่มีการคลิกรายงานสถานะกลับไปที่แผงควบคุมที่มีอยู่ นั่นไม่ใช่กระบวนการง่ายๆ

สิ่งหนึ่งที่ทำให้ฉันผิดหวังคือฉันลืมความแปลกประหลาดบางอย่างในการจัดการ SVG อันดับแรก ฉันพยายามเปลี่ยนสตริงสไตล์เพื่อให้ช่อง display ของสไตล์ CSS เป็น "ไม่มี" หรือ "บางอย่าง" แต่เบราว์เซอร์ไม่เคยเขียนสตริงรูปแบบใหม่ แต่เนื่องจากเป็นเรื่องยุ่งยาก ฉันจึงลองเปลี่ยนคลาส CSS นั่นก็ไม่มีผลเช่นกัน แต่มีแอตทริบิวต์ visibility ซึ่งพวกเราส่วนใหญ่จำได้จาก HTML แบบเก่า (บางทีอาจเป็นเวอร์ชัน 1.0) แต่นั่นเป็นข้อมูลล่าสุดใน SVG และนั่นก็ใช้ได้ดี ทั้งหมดที่ฉันต้องทำคือให้เหตุการณ์คลิกปุ่มเพื่อเผยแพร่

Vue ได้ออกแบบคุณสมบัติเพื่อเผยแพร่ในทิศทางเดียวจากพ่อแม่สู่ลูก ดังนั้น หากต้องการเปลี่ยนข้อมูลในแอปพลิเคชันหรือในแผงควบคุม คุณต้องส่งเหตุการณ์การเปลี่ยนแปลงไปยังพาเรนต์ จากนั้นคุณสามารถเปลี่ยนข้อมูลได้ การเปลี่ยนแปลงขององค์ประกอบข้อมูลที่ควบคุมปุ่มทำให้ Vue อัปเดตคุณสมบัติที่ส่งผลต่อการมองเห็นองค์ประกอบ SVG ที่เราได้เลือกที่จะระบุสถานะ นี่คือตัวอย่าง:

แผงมากกว่าหนึ่งประเภทและอินสแตนซ์แอนิเมชั่นมากกว่าหนึ่งรายการต่อประเภท
สุดท้าย คอลเล็กชันของแผงประเภทต่างๆ แต่ละรายการมีอินสแตนซ์ที่กำหนดให้แยก MCU (ตัวอย่างขนาดใหญ่)

อินสแตนซ์แต่ละอันของแผงปุ่มหยักๆ นั้นแยกจากกัน ดังนั้นบางรายการเปิดอยู่และบางส่วนปิดอยู่

ตัวอย่าง SVG นี้มีตัวบ่งชี้สีเหลืองที่ดูแปลก:

 <path :visibility="stateView" d="m -36.544616,12.266886 c 19.953088,17.062165 5.07961,-19.8251069 5.317463,8.531597 0.237853,28.356704 13.440044,-8.847959 -3.230451,10.779678 -16.670496,19.627638 14.254699,-2.017715 -11.652451,3.586456 -25.90715,5.60417 10.847826,19.889979 -8.095928,-1.546575 -18.943754,-21.436555 -1.177383,14.210702 -4.176821,-12.416207 -2.999438,-26.6269084 -17.110198,8.030902 2.14399,-8.927709 19.254188,-16.9586105 -19.075538,-8.0837048 9.448721,-5.4384245 28.52426,2.6452804 -9.707612,-11.6309807 10.245477,5.4311845 z" transform="translate(78.340803,6.1372042)" />

การมองเห็นถูกเติมโดย stateView ซึ่งเป็นตัวแปรที่คำนวณแล้วซึ่งแมปสถานะบูลีนกับสตริงสำหรับ SVG

นี่คือเทมเพลตข้อกำหนดคอมโพเนนต์แผง:

 <script type="text/x-template"> <div> <control-switch :state="bstate" v-on:changed="saveChanges" ></control-switch> <gauge :level="fluidLevel" ></gauge> </div> </script>

และนี่คือคำจำกัดความ JavaScript ของพาเนล Vue โดยมีลูกเป็นองค์ประกอบย่อย:

 var widgdef = { data: function () { var currentPanel = { // at the top level, values controlling children bstate : true, fluidLevel : Math.round(Math.random()*100) } // return currentPanel }, template: '#mcu-control-panel-template', methods: { saveChanges: function() { // in real life, there is more specificity this.bstate = !this.bstate relayToMCU(this.name,"button",this.bstate) // to be defined } }, components: { 'control-switch' : { // the odd looking button props: ['state'], template: '#control-switch-template', // for demo it is in the page. computed: { // you saw this in the SVG above. stateView : function() { return ( this.state ) ? "visible" : "hidden" } }, methods : { // the button handler is in the SVG template at the top. stateChange : function () { // can send this.$emit('changed'); // tell the parent. See on the template instance } } }, 'gauge' : { // some other nice bit of SVG props: ['level'], template: '#gauge-template' } } }

ดังนั้นตอนนี้กลไกสำหรับปุ่มเดียวที่ฝังอยู่ในแผงควบคุมจึงได้รับการจัดวางแล้ว และต้องมีตะขอในการบอก MCU ว่ามีบางอย่างเกิดขึ้น ต้องเรียกใช้ทันทีหลังจากอัปเดตสถานะข้อมูลของคอมโพเนนต์พาเนลแล้ว มากำหนดกันที่นี่:

 function relayToMCU(panel,switchName,bstate) { var message = switchName + ':' + bstate // a on element parameter string. sendPanelMessage(panel,message) }

มีการเปลี่ยนแปลงสถานะของฮาร์ดแวร์ในโค้ดเพียงสองบรรทัด

แต่นี่เป็นกรณีที่ค่อนข้างง่าย สวิตช์ใด ๆ สามารถดูได้ว่าเป็นฟังก์ชันที่เรียกใช้ชิ้นส่วนของฮาร์ดแวร์ในโลก ดังนั้น สตริงอาจมีชื่อสวิตช์และองค์ประกอบข้อมูลอื่นๆ อีกหลายรายการ ดังนั้น วิธีการของคอมโพเนนต์ที่ลงทะเบียนการเปลี่ยนแปลงจะต้องมีการจัดการแบบกำหนดเองบางอย่างในนั้น เพื่อที่จะสามารถรวบรวมชุดข้อมูลทั้งหมดบนแผงควบคุม และส่งไปพร้อม ๆ กันในสตริงคำสั่งเดียว แม้แต่สตริงคำสั่งก็ค่อนข้างง่าย หาก MCU มีขนาดค่อนข้างเล็ก สตริงคำสั่งอาจต้องแปลเป็นโค้ด หาก MCU มีความสามารถมากมาย สตริงคำสั่งอาจเป็นโครงสร้าง JSON หรืออาจเป็นข้อมูลทั้งหมดที่พาเนลโฮสต์

ในการสนทนานี้ ปุ่มบนแผงไอคอนประกอบด้วยชื่อของแผงที่จะดึงข้อมูล นั่นอาจจะค่อนข้างง่ายเช่นกัน ดูเหมือนว่าจะสมเหตุสมผลที่พารามิเตอร์นั้นสามารถยืนสำหรับพาเนลใด ๆ ที่อาจจัดเก็บไว้ในฐานข้อมูลขององค์กร แต่บางทีมันก็เป็นสูตรบางอย่าง บางที ข้อมูลเกี่ยวกับพาเนลควรล้อมรอบคำจำกัดความพาเนลที่เราได้รับจากเซิร์ฟเวอร์ ไม่ว่าในกรณีใด พื้นฐานสามารถขยายได้อย่างง่ายดายเมื่ออาการปวดหัวบางอย่างหายไป เช่น ทำให้ SVG ตอบสนองต่อการคลิกอย่างเหมาะสม

บทสรุป

การสนทนานี้ได้กำหนดขั้นตอนพื้นฐานและการตัดสินใจบางอย่างที่นำไปสู่การสร้าง Single Page Web App (SPWA) ที่สามารถเชื่อมต่อกับอุปกรณ์ IoT ตอนนี้เรารู้วิธีรับพาเนลจากเว็บเซิร์ฟเวอร์และเปลี่ยนให้เป็นอินเทอร์เฟซ MCU

การสนทนานี้ยังมีอะไรอีกมากพร้อมการสนทนาอื่นๆ ที่อาจตามมาอีกเล็กน้อย การเริ่มต้นด้วย Vue เป็นสิ่งหนึ่งที่ควรคำนึงถึง แต่แล้วก็มีเรื่องราวของ MCU ทั้งหมด ซึ่งเราได้กล่าวถึงเพียงช่วงสั้นๆ เท่านั้น

โดยเฉพาะอย่างยิ่ง โดยการเลือก MQTT เป็นสารตั้งต้นในการสื่อสาร เราถือว่าอุปกรณ์ IoT ในอีกด้านหนึ่งสามารถปกครองโดย MQTT ได้ แต่นั่นอาจไม่เป็นเช่นนั้นเสมอไป บางครั้งจำเป็นต้องใช้เกตเวย์หาก MQTT เพื่อเข้าถึงอุปกรณ์ที่มีลิงก์ซีเรียลหรือบลูทูธ หรือบางทีสิ่งที่ต้องการจากหน้าเว็บก็คือ WebSockets อย่างไรก็ตาม เราใช้ MQTT เป็นตัวอย่างเพื่อแสดงให้เห็นว่า Vue สามารถรับและส่งข้อมูลได้อย่างไร ในขณะที่รักษาสถานะข้อมูลให้ซิงค์กับอุปกรณ์

อีกครั้งที่เรามีเพียงส่วนหนึ่งของเรื่องราว คราวนี้เป็นการทำข้อมูลให้ตรงกันเนื่องจากหน้าเว็บควรจะสามารถจัดการกับการแจ้งเตือนและรบกวนผู้ใช้หากมีเรื่องสำคัญเกิดขึ้น บางครั้งข้อความอาจสูญหายได้ เราจึงต้องมีกลไกในการตอบรับ

สุดท้าย เป็นความเห็นของฉันที่ Vue ทำการอัปเดตข้อมูลเมื่อได้รับข้อมูลที่ค่อนข้างหรูหรา แต่การส่งการเปลี่ยนแปลงสถานะไม่ได้ตรงไปตรงมานัก ดูเหมือนจะไม่ทำให้งานง่ายไปกว่าที่สามารถทำได้ด้วย vanilla JavaScript แต่มีวิธีและมันสมเหตุสมผล

บางทีสามารถสร้างไลบรารีที่สะอาดเพื่อสร้างชุดส่วนประกอบที่เป็นสากลสำหรับแผงทั้งหมดได้ มีการกล่าวถึงองค์ประกอบสำหรับการสร้างไลบรารีดังกล่าวและจัดเก็บไว้ในฐานข้อมูลโดยสังเขป เครื่องมือที่นอกเหนือไปจากการสร้างภาพ SVG อาจต้องได้รับการพัฒนา ไม่ว่าในกรณีใด มีหลายสิ่งหลายอย่างที่สามารถทำได้สำหรับขั้นตอนต่อไป