| RoomModel room.basic.service.timing { |
| |
| import room.basic.types.* from "Types.room" |
| |
| async ActorClass ATimingService { |
| Interface { |
| SPP timer: PTimer |
| } |
| Structure { |
| usercode1 { |
| "#include \"osal/etTime.h\" " |
| "#define ET_NB_OF_TCBS 30" |
| "typedef struct etTCB etTimerControlBlock;" |
| "struct etTCB {" |
| " etTime expTime;" |
| " etTime pTime;" |
| " int32 portIdx;" |
| " etTimerControlBlock* next;" |
| "};" |
| } |
| usercode2 { |
| "// uc2" |
| } usercode3 { |
| "// uc3" |
| } |
| Attribute tcbs [ 30 ]: tcb |
| Attribute usedTcbsRoot: tcb ref |
| Attribute freeTcbsRoot: tcb ref |
| ServiceImplementation of timer |
| } |
| Behavior { |
| ctor { |
| "memset(tcbs.getData(), sizeof(tcbs), 0);" |
| } |
| Operation getTcb(): tcb ref { |
| "etTimerControlBlock* temp = freeTcbsRoot;" |
| "" |
| "if (freeTcbsRoot != 0) {" |
| " freeTcbsRoot = freeTcbsRoot->next;" |
| " temp->next = 0;" |
| "}" |
| "return temp;" |
| "" |
| } |
| Operation returnTcb(block: tcb ref) { |
| "block->next = freeTcbsRoot;" |
| "freeTcbsRoot = block;" |
| } |
| Operation removeTcbFromUsedList(idx: int32) { |
| "etTimerControlBlock* temp = usedTcbsRoot;" |
| "etTimerControlBlock* temp2 = usedTcbsRoot;" |
| "" |
| "if (temp == 0)" |
| " return;" |
| "" |
| "if (usedTcbsRoot->portIdx == idx) {" |
| " /* element found, the first one */" |
| " usedTcbsRoot = usedTcbsRoot->next;" |
| " returnTcb(temp);" |
| " return;" |
| "}" |
| "" |
| "temp = temp->next;" |
| "while (temp != 0) {" |
| " if (temp->portIdx == idx) {" |
| " temp2->next = temp->next;" |
| " returnTcb(temp);" |
| " return;" |
| " } else {" |
| " /* try next */" |
| " temp2 = temp;" |
| " temp = temp->next;" |
| " }" |
| "}" |
| } |
| Operation putTcbToUsedList(block: tcb ref) { |
| "etTimerControlBlock* temp = usedTcbsRoot;" |
| "etTimerControlBlock* temp2 = usedTcbsRoot;" |
| "" |
| "if (temp == 0) {" |
| " /* list empty put new block to root */" |
| " block->next = 0;" |
| " usedTcbsRoot = block;" |
| " return;" |
| "}" |
| "" |
| "while (1) {" |
| " if (temp != 0) {" |
| " if (isTimeGreater(&block->expTime, &temp->expTime)) {" |
| " /* try next position */" |
| " temp2 = temp;" |
| " temp = temp->next;" |
| " } else {" |
| " /* right position found */" |
| " block->next = temp;" |
| " if (temp == usedTcbsRoot) {" |
| " usedTcbsRoot = block;" |
| " } else {" |
| " temp2->next = block;" |
| " }" |
| " return;" |
| " }" |
| " } else {" |
| " /* end of list reached */" |
| " block->next = 0;" |
| " temp2->next = block;" |
| " return;" |
| " }" |
| "}" |
| } |
| Operation isTimeGreater(t1: targetTime ref, t2: targetTime ref): boolean { |
| "if (t1->sec > t2->sec)" |
| " return ET_TRUE;" |
| "if (t1->sec < t2->sec)" |
| " return ET_FALSE;" |
| "if (t1->nSec > t2->nSec)" |
| " return ET_TRUE;" |
| "return ET_FALSE;" |
| } |
| Operation addTime(t1: targetTime ref, t2: targetTime ref) { |
| "t1->sec += t2->sec;" |
| "t1->nSec += t2->nSec;" |
| "while (t1->nSec >= 1000000000L) {" |
| " t1->sec++;" |
| " t1->nSec -= 1000000000L;" |
| "}" |
| } |
| |
| // Operation printList(){ |
| // "etTimerControlBlock* temp=usedTcbsRoot;" |
| // " printf(\"list: \");" |
| // " while (temp!=0){" |
| // " printf(\"(%ld,%ld),\",temp->expTime.sec,temp->expTime.nSec);" |
| // " temp=temp->next;" |
| // " }" |
| // " printf(\"\\n\");" |
| // } |
| |
| StateMachine { |
| Transition tr0: initial -> Operational { |
| action { |
| "int i;" |
| "usedTcbsRoot = 0;" |
| "freeTcbsRoot = &tcbs[0];" |
| "tcbs[ET_NB_OF_TCBS - 1].next = 0;" |
| "for (i = 0; i < ET_NB_OF_TCBS - 1; i++) {" |
| "\ttcbs[i].next = &tcbs[i + 1];" |
| "}" |
| } |
| } |
| Transition tr1: Operational -> Operational { |
| triggers { |
| <startTimeout: timer> |
| } |
| action { |
| "etTimerControlBlock* timer = getTcb();" |
| "etTime t;" |
| "if (timer != 0) {" |
| "\tt.sec = time / 1000;" |
| "\tt.nSec = (time % 1000) * 1000000L;" |
| "\ttimer->pTime.sec = 0;" |
| "\ttimer->pTime.nSec = 0;" |
| "\ttimer->portIdx = ifitem->getIdx();" |
| "\tgetTimeFromTarget(&(timer->expTime));" |
| "\taddTime(&(timer->expTime), &t);" |
| "\tputTcbToUsedList(timer);" |
| "}" |
| } |
| } |
| Transition tr3: Operational -> Operational { |
| triggers { |
| <startTimer: timer> |
| } |
| action { |
| "etTimerControlBlock* timer = getTcb();" |
| "etTime t;" |
| "if (timer != 0) {" |
| "\tt.sec = time / 1000;" |
| "\tt.nSec = (time % 1000) * 1000000L;" |
| "\ttimer->pTime = t;" |
| "\ttimer->portIdx = ifitem->getIdx();" |
| "\tgetTimeFromTarget(&(timer->expTime));" |
| "\taddTime(&(timer->expTime), &t);" |
| "\tputTcbToUsedList(timer);" |
| "}" |
| } |
| } |
| Transition tr4: Operational -> Operational { |
| triggers { |
| <kill: timer> |
| } |
| action { |
| "removeTcbFromUsedList(ifitem->getIdx());" |
| } |
| } |
| State Operational { |
| entry { |
| "// prepare" |
| } do { |
| "// maintain timers" |
| "etTimerControlBlock* temp;" |
| "etTime t;" |
| "" |
| "getTimeFromTarget(&t);" |
| "while (usedTcbsRoot != 0) {" |
| "\tif (isTimeGreater(&t, &(usedTcbsRoot->expTime))) {" |
| "\t\ttimer.get(usedTcbsRoot->portIdx).timeout();" |
| "\t\ttemp = usedTcbsRoot;" |
| "\t\tusedTcbsRoot = usedTcbsRoot->next;" |
| "\t\tif ((temp->pTime.sec == 0) && (temp->pTime.nSec == 0)) {" |
| "\t\t\t// single shot timer" |
| "\t\t\treturnTcb(temp);" |
| "\t\t} else {" |
| "\t\t\t// periodic timer" |
| "\t\t\taddTime(&temp->expTime, &temp->pTime);" |
| "\t\t\tputTcbToUsedList(temp);" |
| "\t\t}" |
| "\t} else {" |
| "\t\tbreak;" |
| "\t}" |
| "}" |
| } |
| } |
| } |
| } |
| } |
| |
| ProtocolClass PTimer { |
| usercode1 { |
| "#define ET_TIMER_RUNNING 0x01" |
| "#define ET_TIMER_PERIODIC 0x02" |
| } |
| usercode2 { |
| "//uc2 " |
| } |
| incoming { |
| Message startTimer(time: uint32) |
| Message startTimeout(time: uint32) |
| Message kill() |
| } |
| outgoing { |
| Message timeout() |
| } |
| conjugated PortClass |
| { |
| Attribute status: int8 = "0" |
| handle |
| incoming startTimer { |
| "if (status==0){ |
| status=ET_TIMER_RUNNING | ET_TIMER_PERIODIC; |
| DebuggingService::getInstance().addMessageAsyncOut(getAddress(), getPeerAddress(), |
| PTimer::getMessageString(PTimer::IN_startTimer)); |
| if (getPeerAddress().isValid()){ |
| getPeerMsgReceiver()->receive(new Message(getPeerAddress(),PTimer::IN_startTimer, |
| &time, |
| sizeof(uint32))); |
| } |
| } |
| " |
| } |
| handle |
| incoming startTimeout { |
| "if (status==0){ |
| status = ET_TIMER_RUNNING; |
| DebuggingService::getInstance().addMessageAsyncOut(getAddress(), getPeerAddress(), |
| PTimer::getMessageString(PTimer::IN_startTimeout)); |
| if (getPeerAddress().isValid()){ |
| getPeerMsgReceiver()->receive(new Message(getPeerAddress(),PTimer::IN_startTimeout, |
| &time, |
| sizeof(uint32))); |
| } |
| } |
| " |
| } |
| handle |
| outgoing timeout { |
| " |
| //TODO: clear active bit in case of single shot timer |
| if (status!=0){ |
| if (status==ET_TIMER_RUNNING){ |
| // single shot timer |
| status=0; |
| } |
| // msg to fsm |
| getActor()->receiveEvent(this, msg->getEvtId(), msg->getData()); |
| } |
| " |
| } |
| handle |
| incoming kill { |
| " |
| if (status!=0){ |
| status=0; |
| DebuggingService::getInstance().addMessageAsyncOut(getAddress(), getPeerAddress(), |
| PTimer::getMessageString(PTimer::IN_kill)); |
| if (getPeerAddress().isValid()){ |
| getPeerMsgReceiver()->receive(new Message(getPeerAddress(),PTimer::IN_kill)); |
| } |
| } |
| " |
| } |
| } |
| } |
| |
| ExternalType tcb -> "etTimerControlBlock" |
| ExternalType targetTime -> "etTime" |
| } |