เครื่องจักรสถานะลูกสามารถปล่อยการควบคุมกลับไปยังเครื่องสถานะหลักได้อย่างไร


9

เครื่องสถานะระดับบนสุดของฉันมีสถานะและขอบบางอย่าง ฉันจะเรียกสิ่งนี้ว่าเครื่องสถานะแม่

A ----> B ----> C

สถานะใด ๆ ภายในเครื่องสถานะแม่สามารถเป็นเครื่องรัฐเกินไป ฉันจะเรียกเด็กเหล่านี้เครื่องจักรรัฐ

           ___________
         /            \
A ----> |  B0->B1->B2  | ----> C
         \____________/

หากเครื่องสถานะแม่เปลี่ยนจาก A ถึง B, เครื่องสถานะของ B ใช้เวลามากกว่า เมื่อ B รันเสร็จสิ้นแล้วมันจะปล่อยการควบคุมไปยังเครื่องสถานะหลักและเปลี่ยนไปใช้สถานะ C ได้อย่างไร คุณใช้รูปแบบการออกแบบแบบใด

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


ฉันเดาว่า B0, B1 และ B2 ควรรู้ว่าพวกเขาเป็นองค์ประกอบของบางสิ่งที่โลกภายนอกคิดว่าเป็นหน่วยเดียว ดังนั้นบางทีคุณต้องมีMachineContainerคลาสสำหรับBที่มี B0, B1, และ B2 และเมื่อ B2 จบลงมันจะผ่านการควบคุมกลับไปที่ตู้คอนเทนเนอร์ซึ่งเปลี่ยนไปเป็น C ... ฉันไม่เคยลองอะไรแบบนี้มาก่อน มันเป็นปัญหาที่น่าสนใจ!
FrustratedWithFormsDesigner

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

คำตอบ:


5

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

เป็นหลักในขณะที่ในรัฐB, จัดการเหตุการณ์หลักของคุณส่งต่อเหตุการณ์ใด ๆ มันไม่รู้จักการB's Bจัดการเหตุการณ์และยังคงอยู่ในสถานะ เมื่อBต้องการเปลี่ยนCเป็นโพสต์เหตุการณ์ที่เหมาะสมไปยังตัวจัดการเหตุการณ์หลัก


2

คุณอ่านหัวข้อของ Taoup นี้หรือยัง? มีหลายวิธีในการทำสิ่งนี้ แต่หลายวิธีขึ้นอยู่กับว่าคุณแบ่งเครื่องของคุณอย่างไร พวกเขาแยกกระบวนการหรือไม่ หัวข้อ? วัตถุ?

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

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


1

แยกเครื่องจักรสถานะสองเครื่องและใช้การส่งข้อความระหว่างกัน ดังนั้น state machine 1 จะเริ่มต้นจาก ABC โดยที่ state B จะตรวจสอบผลลัพธ์ปัจจุบันจาก state machine 2 หากผลลัพธ์มีการเปลี่ยนแปลงแล้ว state machine 1 สามารถอธิบายได้และ State machine 2 ไม่จำเป็นต้องรับรู้ใด ๆ การทำงานของกลไกสถานะ 1 ใช้งานได้จริง สิ่งที่ต้องการ:

typedef struct StateMachine {
  void(*Update)(); // function to update the state machine
  int Data;        // generic temp holder to survive state contexts
  int State;       // current state of our state machine
  int *Message;    // pointer to a shared integer for message passing
};

int main(void) {
  int Message = 0;
  /* NewStateMachine would malloc the struct, pass in the int reference
   * and function pointer as well as add it to a circularly linked list */
  NewStateMachine(&Message, MainLoop);
  NewStateMachine(&Message, MinorLoop);
  StateMachine *Current = StateMachine_CLL.First;

  for(;;) {
    Current->Update(Current); /* Update the current state machine */
    Current = Current->Next;  /* And the advance to the next one */
  }
}

void MainLoop(StateMachine *this) {
  switch(this.State) {
  case 0:
    CloseCoolantTank(1); /* safe to call if valve already closed */
    CloseCoolantTank(2); /* safe to call if valve already closed */
    this.State = 1;
    break;
  case 1:
    /* we have a message, do something */
    if(*this.Message) this.State = 2;          
    /* otherwise stall at this state until we get a message */
    else this.State = 1;          
    break;
  case 2:
    if(*this.Message == 1) this.State = 3;      /* warm */
    else if(*this.Message == 2) this.State = 4; /* hot! */
    else this.State = 0;                        /* cooled down, shut off valves */
    this.Message = 0;                           /* clear the message */
    break;
  case 3:
    OpenCoolantTank(1); /* opens the valve, safe to call if already open */
    this.State = 2;     /* recheck for new message */
    break;
  case 4:
    OpenCoolantTank(2); /* opens the valve, safe to call if already open */
    this.State = 3;     /* also open coolant tank 1 for extra cooling */
    break;
  }
}

/* Monitor temperature and send messages on overheat */
void MinorLoop(StateMachine *this) {
  switch(this.State) {
  case 0:
    this.Data = ReadADCValue();
    this.State = 1;
    break;
  case 1:
    if(this.Data > 150) *this.Message = 2;
    else if(this.Data > 100) *this.Message = 1;
    this.State = 0;
    break;
  }
}

1

วิธีการแก้ปัญหาขึ้นอยู่กับ 1) ว่าสถานะย่อยของ A นั้นสามารถมองเห็นได้ในสถานะย่อยของ B หรือไม่ 2) ทำ AB และ C มาจากผู้ปกครองทั่วไป หากพวกเขามีผู้ปกครองทั่วไปและการมองเห็นเป็นสากลคุณไม่ควรมีปัญหามากเกินไปในการย้ายจากสถานะย่อยของ B ไปเป็นสถานะย่อยของ A

หากคุณแยกพวกเขาผ่านเนมสเปซและ / หรือ A, B และ C ไม่มีพ่อแม่ทั่วไปแล้ว wayy ที่ดีที่สุดของคุณคือการมีไดรเวอร์การเปลี่ยนสถานะภายนอกสำหรับเครื่อง A, B และ C สิ่งนี้สามารถทำได้ผ่านตัวจัดการเหตุการณ์ เพียงแค่มีผู้สังเกตการณ์ใน A ซึ่งสามารถรับฟังเหตุการณ์ที่เกิดขึ้นใน B และการเปลี่ยนสถานะย่อยของตัวเองตามเหตุการณ์

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