วิธีจัดการสถานะเริ่มต้นในสถาปัตยกรรมที่ขับเคลื่อนด้วยเหตุการณ์ได้อย่างไร


33

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

ลองนึกภาพรถยนต์สมมุติกับแป้นเบรกและไฟเบรก

  • ไฟเบรคจะเปิดขึ้นเมื่อได้รับเหตุการณ์brake_onและปิดเมื่อได้รับเหตุการณ์brake_off
  • แป้นเบรกส่งเหตุการณ์brake_onเมื่อมันถูกกดลงและเหตุการณ์brake_offเมื่อมันถูกปล่อยออกมา

ทั้งหมดนี้เป็นสิ่งที่ดีและดีจนกระทั่งคุณมีสถานการณ์ที่รถเปิดอยู่โดยเหยียบแป้นเบรกลงไปแล้ว เนื่องจากไฟเบรกไม่เคยได้รับเหตุการณ์brake_onมันจะหยุดอยู่ - เป็นสถานการณ์ที่ไม่พึงประสงค์อย่างชัดเจน การเปิดไฟเบรคตามค่าเริ่มต้นจะเป็นการพลิกกลับสถานการณ์เท่านั้น

สิ่งที่สามารถทำได้เพื่อแก้ไข 'ปัญหาสถานะเริ่มต้น' นี้

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

แก้ไข 2:นอกเหนือจากคำตอบของ @ gbjbaanbฉันจะใช้ระบบที่:

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

ด้วยโซลูชันนี้ไม่มีการพึ่งพาระหว่างคอมโพเนนต์ไม่มีสภาวะการแข่งขันไม่มีคิวข้อความให้ค้างและไม่มีคอมโพเนนต์ 'ต้นแบบ'


2
สิ่งแรกที่ควรคำนึงถึงคือการสร้างเหตุการณ์ "สังเคราะห์" (เรียกว่าinitialize) ซึ่งมีข้อมูลเซ็นเซอร์ที่จำเป็น
msw

คันเหยียบไม่ควรส่งเหตุการณ์ brake_pedal_on และเบรกจริงส่งเหตุการณ์ brake_on หรือไม่ ฉันไม่ต้องการให้ไฟเบรกติดถ้าเบรคไม่ทำงาน
bdsl

3
ฉันเคยพูดว่ามันเป็นตัวอย่างสมมุติหรือไม่? :-) มันง่ายมากที่จะทำให้คำถามสั้นและตรงประเด็น
Frank Kusters

คำตอบ:


32

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

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

ดังนั้นเมื่อเหยียบสวิตช์กุญแจจะได้รับข้อความลงทะเบียน / ตรวจสอบจากการจัดการรถยนต์และมันจะกลับมาไม่เพียง แต่ข้อความ "ฉันอยู่ที่นี่และทำงาน" แต่มันจะตรวจสอบสถานะของตัวเองและส่ง ข้อความสำหรับสถานะนั้น (เช่นข้อความที่เหยียบต่ำ)

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

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


1
ฉันชอบคำตอบของคุณเนื่องจากมันเก็บส่วนประกอบทั้งหมดแยกจากกัน - เป็นเหตุผลที่สำคัญที่สุดในการเลือกสถาปัตยกรรมนี้ อย่างไรก็ตามในปัจจุบันไม่มีองค์ประกอบ 'ต้นแบบ' จริงที่ตัดสินใจว่าระบบอยู่ในสถานะ 'เริ่มต้นได้' - ทุกอย่างเพิ่งเริ่มทำงาน ด้วยปัญหาในคำถามของฉันเป็นผล เมื่อต้นแบบตัดสินใจว่าระบบกำลังทำงานอยู่มันสามารถส่งเหตุการณ์ 'เริ่มต้นระบบ' ไปยังคอมโพเนนต์ทั้งหมดหลังจากนั้นทุกคอมโพเนนต์เริ่มออกอากาศสถานะ แก้ไขปัญหา. ขอขอบคุณ! (ตอนนี้ฉันเพิ่งเหลือปัญหาวิธีการตัดสินใจว่าระบบจะเริ่มต้น ... )
Frank Kusters

วิธีการเกี่ยวกับการมอบหมายงานการปรับปรุงสถานะติดตามการปรับปรุงล่าสุดที่ได้รับจากแต่ละวัตถุและเมื่อใดก็ตามที่ได้รับการร้องขอการสมัครสมาชิกใหม่ได้รับมันส่งสมาชิกใหม่การปรับปรุงล่าสุดที่ได้รับจากแหล่งเหตุการณ์ที่ลงทะเบียน?
supercat

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

@ spaceknarf เป็นอย่างดีในกรณีที่ "ทุกอย่างเพิ่งเริ่มทำงาน" คุณไม่สามารถสร้างการพึ่งพาในส่วนต่าง ๆ ได้ดังนั้นเหยียบจะเริ่มขึ้นหลังจากแสงไฟคุณจะต้องเริ่มต้นใหม่ตามลำดับแม้ว่าฉันจะคิดว่ามีบางอย่างเริ่มทำงาน พวกเขาในลำดับ 'ถูกต้อง' (เช่นสคริปต์เริ่มต้น linux เริ่มต้นก่อน systemd ซึ่งบริการที่จะเริ่มก่อนเรียกว่า 1.xxx และที่สองเรียกว่า 2.xxx เป็นต้น)
gbjbaanb

สคริปต์ที่มีคำสั่งเช่นนั้นบอบบาง มันมีการอ้างอิงโดยนัยจำนวนมาก ฉันคิดว่าคุณมีองค์ประกอบ 'master' ที่มีรายการส่วนประกอบที่กำหนดค่าแบบสแตติกที่ควรรัน (ดังที่ @Lie Ryan กล่าวไว้) จากนั้นมันสามารถออกอากาศเหตุการณ์ 'พร้อม' เมื่อโหลดส่วนประกอบทั้งหมดแล้ว ในการตอบสนองนั้นองค์ประกอบทั้งหมดจะออกอากาศสถานะเริ่มต้น
Frank Kusters

4

คุณสามารถเริ่มต้นเหตุการณ์ซึ่งกำหนดสถานะอย่างเหมาะสมเมื่อโหลด / เริ่มต้น สิ่งนี้อาจเป็นที่ต้องการสำหรับระบบที่เรียบง่ายหรือโปรแกรมที่ไม่รวมฮาร์ดแวร์หลายชิ้น แต่สำหรับระบบที่ซับซ้อนมากขึ้นที่มีองค์ประกอบทางกายภาพหลายอย่างในขณะที่คุณมีความเสี่ยงเช่นเดียวกับการไม่เริ่มต้นเลย - หากมีเหตุการณ์ ระบบ (ตัวอย่างเช่นระบบที่ใช้ CAN) คุณอาจตั้งค่าระบบของคุณไปข้างหลังโดยไม่ได้ตั้งใจราวกับว่าคุณเริ่มต้นทำงานโดยที่เบรกหยุดทำงาน คุณอาจมีตัวควบคุมมากขึ้นเช่นมีรถยนต์สิ่งที่เป็นไปได้ที่จะพลาดโอกาสที่สูงกว่านั้นก็คือ

ในการบัญชีสำหรับสิ่งนี้คุณสามารถให้ตรรกะ "เบรคกับ" ส่งซ้ำ "เบรคบน" เหตุการณ์ บางทีทุก ๆ 1/100 วินาทีหรือบางอย่าง รหัสของคุณที่มีสมองสามารถฟังเหตุการณ์เหล่านี้และเรียก "เบรก" ในขณะที่กำลังรับมัน หลังจาก 1 / 10sec ที่ไม่ได้รับสัญญาณ "brake on" จะส่งผลให้เกิดเหตุการณ์ "brake_off" ภายใน

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

ความซับซ้อนของระบบทางกายภาพของคุณจะกำหนดว่าวิธีการใดที่เหมาะสมกว่า จากตัวอย่างของคุณคือยานพาหนะคุณอาจต้องการบางสิ่งที่คล้ายกัน

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


ในระบบทางกายภาพคุณจะต้องใช้ลวดและใช้ลอจิกแบบไบนารี่: HIGH มีการกดเบรกและ LOW ไม่มีการเบรก
ratchet freak

@ ratchetfreak มีความเป็นไปได้มากมายสำหรับสิ่งประเภทนี้ บางทีสวิตช์สามารถจัดการกับสิ่งนั้นได้ มีเหตุการณ์อื่น ๆ ของระบบจำนวนมากที่ไม่ได้จัดการอย่างนั้น
enderland

1

ในกรณีนี้ฉันจะไม่เบรคแบบง่าย ๆ เป็นเปิด / ปิด ค่อนข้างฉันจะส่งเหตุการณ์ "ความดันเบรก" ตัวอย่างเช่นความดันที่ 0 จะบ่งบอกถึงการปิดและความดันที่ 100 จะถูกกดดันอย่างเต็มที่ ระบบ (โหนด) จะส่งเหตุการณ์ความดันเบรก (ในช่วงเวลาหนึ่ง) ไปยังคอนโทรลเลอร์อย่างต่อเนื่องตามต้องการ

เมื่อระบบเริ่มทำงานมันจะเริ่มรับเหตุการณ์ความดันจนกว่าจะปิด


1

หากวิธีการเดียวในการส่งผ่านข้อมูลสถานะของคุณคือผ่านเหตุการณ์ต่าง ๆ แสดงว่าคุณกำลังมีปัญหา แต่คุณจะต้องสามารถ:

  1. สอบถามสถานะปัจจุบันของแป้นเบรกและ
  2. ลงทะเบียนสำหรับกิจกรรม "เปลี่ยนสถานะ" จากแป้นเบรก

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

เมื่อระบบเริ่มทำงานไฟเบรกจะลงทะเบียนพร้อมกับแป้นเบรกเพื่อรับการแจ้งเตือนการเบรก และอ่านสถานะปัจจุบันของแป้นเบรกและเปิดหรือปิดตัวเอง

จากนั้นการแจ้งเตือนการเบรกสามารถดำเนินการได้หนึ่งในสามวิธี:

  1. เป็นเหตุการณ์ "การเปลี่ยนสถานะเบรกเหยียบ" แบบไม่ใช้พารามิเตอร์
  2. เป็นคู่ของ "เหยียบเบรกตอนนี้หดหู่" และ "เหยียบเบรกออกตอนนี้" เหตุการณ์
  3. เป็นเหตุการณ์ "สภาพเหยียบคันเร่งใหม่" พร้อมพารามิเตอร์ "หดหู่" หรือ "ปล่อย"

ฉันชอบวิธีแรกซึ่งหมายความว่าเมื่อได้รับการแจ้งเตือนไฟเบรกจะทำสิ่งที่รู้แล้วว่าต้องทำอย่างไร: อ่านสถานะปัจจุบันของแป้นเบรกและเปิดหรือปิดตัวเอง


0

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

สิ่งสำคัญคือต้องมีสถานะเริ่มต้นบางอย่าง ไฟเบรกของคุณจะใช้สถานะเริ่มต้นเป็น 'ปิด' และแป้นเบรกของคุณจะใช้สถานะเริ่มต้นเป็น 'ขึ้น' การเปลี่ยนแปลงใด ๆ หลังจากนั้นจะเป็นเหตุการณ์

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

ฉันยังพบว่ามันอึดอัดใจที่จะใช้สองเหตุการณ์ที่แตกต่างกันสำหรับสิ่งที่เป็นได้อย่างมีประสิทธิภาพในสิ่งเดียวกัน brake_offและbrake_onสามารถทำให้e_brakeเป็นพารามิเตอร์bool onได้ง่ายขึ้น คุณสามารถทำให้กิจกรรมของคุณง่ายขึ้นด้วยการเพิ่มข้อมูลสนับสนุน


0

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

การใช้ใบแจ้งหนี้จะทำให้เกิดปัญหาหนึ่งซึ่งคือขนาดกล่องจดหมาย คุณไม่ต้องการให้ระบบเก็บข้อความจำนวนมากขึ้นเรื่อย ๆ สำหรับส่วนประกอบที่จะไม่ออนไลน์อีกต่อไป นี่เป็นสิ่งสำคัญโดยเฉพาะอย่างยิ่งกับระบบฝังตัวที่มีข้อ จำกัด ของหน่วยความจำที่เข้มงวด เพื่อเอาชนะขีด จำกัด ขนาดกล่องจดหมายข้อความที่ออกอากาศทั้งหมดจะต้องปฏิบัติตามกฎสองสามข้อ กฎคือ:

  1. ทุกกิจกรรมการออกอากาศต้องมีชื่อ
  2. ในเวลาที่กำหนดผู้ส่งเหตุการณ์ออกอากาศสามารถมีการออกอากาศที่ใช้งานได้เพียงหนึ่งรายการที่มีชื่อที่ระบุ
  3. ผลกระทบที่เกิดจากเหตุการณ์ต้องเป็น idempotent

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

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

นอกจากนี้เพื่อจัดการกับสถานการณ์ที่ตัวเราเตอร์ข้อความนั้นออฟไลน์คุณต้องมีกล่องข้อความด้วย ข้อความขาออกเป็นส่วนหนึ่งของส่วนประกอบที่เก็บข้อความที่ส่งออกชั่วคราว

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.