home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1996-03-20 | 30.6 KB | 924 lines | [ TEXT/MPS ]
// Text of project Altered States written on 3/20/96 at 1:02 PM // Beginning of file protoEvent // Before Script for "_userproto000" // Newton Developer Technical Support Sample Code // protoEvent - An NTK Finite State Machine User Proto // by Jim Schram, Newton Developer Technical Support // Copyright ©1996 Apple Computer, Inc. All rights reserved. // // You may incorporate this sample code into your applications without // restriction. This sample code has been provided "AS IS" and the // responsibility for its operation is 100% yours. You are not // permitted to modify and redistribute the source as "DTS Sample Code." // If you are going to re-distribute the source, we require that you // make it clear in the source that the code was descended from // Apple-provided sample code, but that you've made changes. _userproto000 := {viewBounds: {left: 8, top: 16, right: 96, bottom: 32}, _proto: @218}; constant |layout_protoEvent| := _userproto000; // End of file protoEvent // Beginning of file protoState // Before Script for "_userproto001" // Newton Developer Technical Support Sample Code // protoState - An NTK Finite State Machine User Proto // by Jim Schram, Newton Developer Technical Support // Copyright ©1996 Apple Computer, Inc. All rights reserved. // // You may incorporate this sample code into your applications without // restriction. This sample code has been provided "AS IS" and the // responsibility for its operation is 100% yours. You are not // permitted to modify and redistribute the source as "DTS Sample Code." // If you are going to re-distribute the source, we require that you // make it clear in the source that the code was descended from // Apple-provided sample code, but that you've made changes. _userproto001 := {viewBounds: {left: 8, top: 16, right: 112, bottom: 56}, _proto: @473}; constant |layout_protoState| := _userproto001; // End of file protoState // Beginning of file protoFSM // Before Script for "_userproto002" // Newton Developer Technical Support Sample Code // protoFSM - An NTK Finite State Machine User Proto // by Jim Schram, Newton Developer Technical Support // Copyright ©1996 Apple Computer, Inc. All rights reserved. // // You may incorporate this sample code into your applications without // restriction. This sample code has been provided "AS IS" and the // responsibility for its operation is 100% yours. You are not // permitted to modify and redistribute the source as "DTS Sample Code." // If you are going to re-distribute the source, we require that you // make it clear in the source that the code was descended from // Apple-provided sample code, but that you've made changes. kFSMCleanUpFunc := func(fsmFrame) begin local fsmSymbol, stateSymbol, eventSymbol, hasGenesisState; RemoveSlot(fsmFrame._proto, '_proto); RemoveSlot(fsmFrame._proto, 'viewBounds); RemoveSlot(fsmFrame, 'viewBounds); fsmFrame.fsm_private_states := { }; if not fsmSymbol := GetSlot(fsmFrame, 'declareSelf) then begin fsmSymbol := 'unknown; print("A protoFSM implementation is unnamed (you forgot the declareSelf slot)."); end; if fsmFrame.stepChildren then foreach stateFrame in fsmFrame.stepChildren do begin RemoveSlot(stateFrame, '_proto); RemoveSlot(stateFrame, 'viewBounds); if not stateSymbol := GetSlot(stateFrame, 'declareSelf) then begin stateSymbol := 'unknown; print("A protoState in the '|" & fsmSymbol & "| protoFSM implementation is unnamed (you forgot the declareSelf slot)."); end; if fsmFrame.fsm_private_states.(stateSymbol) then print("The '|" & stateSymbol & "| protoState in the '|" & fsmSymbol & "| protoFSM implementation already exists (duplicate declareSelf slot value)."); else fsmFrame.fsm_private_states.(stateSymbol) := stateFrame; if stateFrame.stepChildren then foreach eventFrame in stateFrame.stepChildren do begin RemoveSlot(eventFrame, '_proto); RemoveSlot(eventFrame, 'viewBounds); if not eventSymbol := GetSlot(eventFrame, 'declareSelf) then begin eventSymbol := 'unknown; print("A protoEvent in the '|" & stateSymbol & "| state of the '|" & fsmSymbol & "| protoFSM implementation is unnamed (you forgot the declareSelf slot)."); end; if stateFrame.(eventSymbol) then print("The '|" & eventSymbol & "| protoEvent in the '|" & stateSymbol & "| protoState in the '|" & fsmSymbol & "| protoFSM implementation already exists (duplicate declareSelf slot value)."); else stateFrame.(eventSymbol) := eventFrame; RemoveSlot(eventFrame, 'declareSelf); end; RemoveSlot(stateFrame, 'declareSelf); RemoveSlot(stateFrame, 'stepChildren); hasGenesisState := hasGenesisState or stateSymbol = 'Genesis; end; if not hasGenesisState then print("The '|" & fsmSymbol & "| protoFSM implementation is missing the required '|Genesis| state."); if not kDebugOn then // GoToState is a debug-only function! begin RemoveSlot(fsmFrame._proto, 'GoToState); RemoveSlot(fsmFrame, 'GoToState); end; RemoveSlot(fsmFrame, 'stepChildren); end _userproto002 := {viewBounds: {left: 8, top: 8, right: 128, bottom: 72}, DoEvent: func(eventSymbol, paramArray) // SELF can be anything that inherits to the finite state machine instance frame begin local x := fsm_private_context; if not x then // this catches the situation where the FSM is disposed before a pending delayed action/call/send executes return; if kDebugOn then if paramArray and PrimClassOf(paramArray) <> 'Array then Throw('|evt.ex.msg|, "protoFSM:DoEvent 2nd argument must be Nil or Array"); x.pendingEventQueue:EnQueue(eventSymbol); x.pendingParamsQueue:EnQueue(paramArray); if not x.busy then begin x.busy := true; AddDelayedSend(x.fsm, 'DoEvent_Loop, nil, x.turtle); end; nil; end, instantiate: // SELF is the finite state machine template frame, e.g.: // // local fsm := GetLayout("myFSM"):Instantiate(); // // "myFSM" is assumed to be a layout based on protoFSM, func() begin local obj := { _proto: self, fsm: nil, currentStateFrame: nil, currentEventFrame: nil, fsm_private_context: { fsm: nil, turtle: 1, level: 0, busy: nil, waitView: nil, waitAborted: nil, pendingState: 'Genesis, pendingEventQueue: QueueTemplate:Instantiate(), pendingParamsQueue: QueueTemplate:Instantiate(), currentState: nil, currentEvent: nil, currentParams: nil, }, }; obj.currentStateFrame := { _proto: obj.fsm_private_states.Genesis, _parent: obj, }; obj.fsm := obj.fsm_private_context.fsm := obj; end, dispose: func() // SELF is the finite state machine instance frame begin fsm_private_context.pendingEventQueue:Reset(); fsm_private_context.pendingParamsQueue:Reset(); foreach slot in fsm_private_context do fsm_private_context.slot := nil; currentStateFrame := currentEventFrame := fsm_private_context := nil; // guaranteed to return nil so that the caller can conveniently nil out the FSM container variable end, DoEvent_Loop: func() // SELF is the finite state machine instance frame begin local x := fsm_private_context; if not x then // this catches the situation where the FSM is disposed before a pending delayed action/call/send executes return; local ok; local pendingStateFrame; if x.pendingState then if fsm_private_states.(x.pendingState) then if fsm_private_states.(x.pendingState).(x.pendingEventQueue:Peek()) then ok := true; else begin if kDebugOn then :?DebugFSM('UnknownEvent, x.pendingState, x.pendingEventQueue:Peek(), x.pendingParamsQueue:Peek()); // ignore if event not programmed end; else begin if kDebugOn then :?DebugFSM('UnknownState, x.pendingState, x.pendingEventQueue:Peek(), x.pendingParamsQueue:Peek()); // error --> remain in current state end; else begin if kDebugOn then :?DebugFSM('NilState, x.pendingState, x.pendingEventQueue:Peek(), x.pendingParamsQueue:Peek()); // machine halted end; if not ok then begin currentStateFrame := nil; currentEventFrame := nil; x.pendingEventQueue:DeQueue(); // there is a problem with this state or event x.pendingParamsQueue:DeQueue(); // so remove the offending pending queue elements end; else begin x.currentState := x.pendingState; x.currentEvent := x.pendingEventQueue:DeQueue(); x.currentParams := x.pendingParamsQueue:DeQueue(); currentStateFrame := { _proto: fsm_private_states.(x.currentState), _parent: self, }; currentEventFrame := { _proto: fsm_private_states.(x.currentState).(x.currentEvent), _parent: currentStateFrame, }; if currentEventFrame.Action then begin if kDebugOn then :?TraceFSM('PreAction, x.currentState, x.currentEvent, x.currentParams); x.level := x.level + 1; try Perform(currentEventFrame, 'Action, x.currentParams); onexception |evt.ex| do begin try :?ExceptionHandler(CurrentException()); onexception |evt.ex| do nil; end; x.level := x.level - 1; if kDebugOn then :?TraceFSM('PostAction, x.currentState, x.currentEvent, x.currentParams); end; if currentEventFrame.nextState exists then if not x.pendingState := currentEventFrame.nextState then if kDebugOn then :?DebugFSM('NilNextState, x.currentState, x.currentEvent, x.currentParams); // machine halted if kDebugOn then :?TraceFSM('NextState, x.pendingState, x.pendingEventQueue:Peek(), x.pendingParamsQueue:Peek()); end; if x.waitView // check for terminal state & exit waitView if necessary and x.pendingState and pendingStateFrame := fsm_private_states.(x.pendingState) then if pendingStateFrame.terminal then begin x.pendingEventQueue:Reset(); x.pendingParamsQueue:Reset(); AddDelayedCall( func() if x.waitView then x.waitView:Close(), nil, 1 ); end; if x.pendingEventQueue:IsEmpty() then x.busy := nil; else AddDelayedSend(self, 'DoEvent_Loop, nil, x.turtle); nil; end, SetSpeed: func(newSpeed) // SELF is the finite state machine instance frame begin fsm_private_context.turtle := newSpeed; end, IsBusy: func() // SELF is the finite state machine instance frame begin fsm_private_context.busy; end, GetSpeed: func() // SELF is the finite state machine instance frame begin fsm_private_context.turtle; end, GoToState: // SELF is the finite state machine instance frame // This function is for DEBUGGING USE ONLY ! ! ! // It is STRIPPED from the resulting package when kDebugOn = nil func(newState) begin local x := fsm_private_context; x.pendingState := newState; x.pendingEventQueue:Reset(); x.pendingParamsQueue:Reset(); nil; end, QueueTemplate: { Instantiate: func() // This is a very simple First-In-First-Out queue { _proto: self, queue: [], }, Reset: func() SetLength(queue, 0), Peek: func() if Length(queue) > 0 then queue[0], // else nil DeQueue: func() if Length(queue) > 0 then // else nil begin local data := queue[0]; RemoveSlot(queue, 0); data; end, EnQueue: func(data) begin AddArraySlot(queue, data); nil; end, GetQueueSize: func() Length(queue), IsEmpty: func() Length(queue) = 0, }, ProtoClone: func(object) begin local f := func native(obj) begin if not IsFrame(obj) or IsFunction(obj) then Throw('|evt.ex.msg|, "ProtoClone only works with frames."); local new := {_proto: obj}; foreach slot, value in obj do if IsFrame(value) and not IsFunction(value) then new.(slot) := call f with (value); new; end; call f with (object); end, WaitForTerminal: func(options) begin local x := fsm_private_context; if x.waitView or x.level <> 0 or x.pendingEventQueue:IsEmpty() then return; x.waitView := BuildContext(waitViewTemplate); x.waitView:SetOwnerContext(x, options); x.waitView:ModalDialog(); x.waitAborted; // return the value of waitAborted (true = user aborted via the status slip, nil = FSM terminal state was reached normally) end, waitViewTemplate: { viewClass: clView, viewFlags: vVisible, viewFormat: vfNone, viewBounds: { left: 0, top: 0, right: 0, bottom: 0, }, statusView: nil, statusViewOptions: nil, fsmContext: nil, aborted: nil, SetOwnerContext: func(owner, options) begin self.statusView := nil; self.statusViewOptions := options; // frame of options the caller of WaitForTerminal is passing us (e.g. progress messages, etc.) self.fsmContext := owner; // the fsm_private_context slot of the FSM self.aborted := nil; end, viewIdleScript: func() begin statusView := BuildContext(statusViewTemplate); statusView:SetOwnerContext(self, statusViewOptions); statusView:ModalDialog(); nil; end, viewSetupDoneScript: func() begin inherited:?ViewSetupDoneScript(); if not statusViewOptions then :SetUpIdle(2000); else if statusViewOptions.delayUntilStatusVisible then :SetUpIdle(if statusViewOptions.delayUntilStatusVisible <= 0 then 1 else statusViewOptions.delayUntilStatusVisible); end, viewQuitScript: func() begin if statusView then statusView:Close(); fsmContext.waitAborted := aborted; fsmContext.waitView := nil; end, statusViewTemplate: { _proto: protoStatusTemplate, initialSetup: nil, waitView: nil, delayUntilAbortTimer: nil, delayUntilAbortVisible: nil, abortButtonText: nil, viewIdleScript: func() begin inherited:?viewIdleScript(); local contents := { name: 'vBarber, values: { barber: true, }, }; if delayUntilAbortVisible then begin delayUntilAbortTimer := delayUntilAbortTimer + 300; if delayUntilAbortTimer > delayUntilAbortVisible then begin delayUntilAbortVisible := nil; contents.values.primary := { text: abortButtonText, script: func() begin waitView.aborted := true; waitView:Close(); end, }; base:ViewSet(contents); return 300; end; end; base:UpdateIndicator(contents); 300; end, viewSetupDoneScript: func() begin inherited:?ViewSetupDoneScript(); :SetUpIdle(100); self.delayUntilAbortTimer := 0; end, SetOwnerContext: func(owner, options) begin self.waitView := owner; self.delayUntilAbortVisible := if options then options.delayUntilAbortVisible else 8000; self.abortButtonText := if options and options.abortButtonText then options.abortButtonText else "Abort"; self.initialSetup := { name: 'vBarber, values: { icon: ROM_routeUpdateBitmap, statusText: if options then options.statusText else "Please wait...", titleText: if options then options.titleText else nil, barber: true, primary: nil, closeBox: nil, }, }; end, }, }, ExceptionHandler: func(exception) begin local x := fsm_private_context; local message := if x then "The following exception occured in event (" & x.currentEvent & ") of state (" & x.currentState & ") of finite state machine (" & x.fsm.declareSelf & "): " else "The following exception occured: "; local exceptionStr := "<unable to create string representation of exception frame>"; try exceptionStr := :ObjectToString(exception); onexception |evt.ex| do nil; GetRoot():Notify(kNotifyAlert, kAppName, message & exceptionStr); print(message); print(exception); if GetGlobalVar('BreakOnThrows) then BreakLoop(); nil; end, ObjectToString: // Converts almost any NewtonScript data type into a string representation. // Does NOT handle recursive/self-referencing frames. // Does NOT follow _proto & _parent pointers. // Does NOT check for out-of-memory conditions, bad object refs, et cetera. func(obj) begin local separator := ", "; local separatorLen := StrLen(separator); local p := func(s) if EndsWith(s, separator) then StrMunger(s, StrLen(s) - separatorLen, nil, nil, 0, nil) else s; local f := func(obj) begin ( if IsFunction(obj) then "func(" & NumberStr(GetFunctionArgCount(obj)) & (if GetFunctionArgCount(obj) = 1 then " arg)" else " args)") else if IsFrame(obj) then begin local s := "{"; foreach slot, item in obj do s := s & SPrintObject(slot) & ": " & if slot = '_parent or slot = '_proto then "<ignored>" & separator else call f with (item); call p with (s) & "}"; end else if IsArray(obj) then begin local s := "["; foreach item in obj do s := s & call f with (item); call p with (s) & "]"; end else if IsString(obj) then $" & obj & $" else if IsSymbol(obj) then $' & obj else if IsNumber(obj) or IsInteger(obj) then NumberStr(obj) else if IsImmediate(obj) then if not obj then "nil" else if obj = true then "true" else SPrintObject(obj) else SPrintObject(obj) ) & separator; end; call p with (call f with (obj)); end, _proto: @218 }; constant |layout_protoFSM| := _userproto002; // End of file protoFSM // Beginning of file ExampleFSM // Before Script for "Example FSM" // Newton Developer Technical Support Sample Code // ExampleFSM - An NTK Finite State Machine User Proto Implementation // by Jim Schram, Newton Developer Technical Support // Copyright ©1996 Apple Computer, Inc. All rights reserved. // // You may incorporate this sample code into your applications without // restriction. This sample code has been provided "AS IS" and the // responsibility for its operation is 100% yours. You are not // permitted to modify and redistribute the source as "DTS Sample Code." // If you are going to re-distribute the source, we require that you // make it clear in the source that the code was descended from // Apple-provided sample code, but that you've made changes. Example FSM := {viewBounds: {left: 8, top: 8, right: 152, bottom: 272}, DebugFSM: // If this method is defined it will be called as an aid in debugging your finite state machine. // // reason = one of 'UnknownState, 'UnknownEvent, 'NilState, 'NilNextState // state = the current state symbol // event = the current event symbol // params = the parameter array for the event Action function func(reason, state, event, params) begin local s := "Reason = " & :ObjectToString(reason) & "\nState = " & :ObjectToString(state) & "\nEvent = " & :ObjectToString(event) & "\nParams = " & :ObjectToString(params); :MTrace(s); print(SubstituteChars(s, "\n", "\t")); end, TraceFSM: // If this method is defined it will be called as an aid in debugging your finite state machine. // // when = one of 'PreAction, 'PostAction, 'NextState // state = the current state symbol // event = the current event symbol // params = the parameter array for the event Action function func(when, state, event, params) begin local s := "When = " & :ObjectToString(when) & "\nState = " & :ObjectToString(state) & "\nEvent = " & :ObjectToString(event) & "\nParams = " & :ObjectToString(params); :MTrace(s); print(SubstituteChars(s, "\n", "\t")); end, declareSelf: 'ExampleFSM, ExceptionHandler: func(exception) begin inherited:?ExceptionHandler(exception); // Implement your own exception handler here, or use the cheesy default one provided by protoFSM. end, MTrace: func(s) begin SetValue(GetRoot().(kAppSymbol).vTraceBox, 'text, s); RefreshViews(); end, fTheAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything: nil, debug: "Example FSM", _proto: _userproto002 }; Genesis := {declareSelf: 'Genesis, viewBounds: {left: 8, top: 16, right: 136, bottom: 56}, terminal: true, debug: "Genesis", _proto: _userproto001 }; AddStepForm(Example FSM, Genesis); Create := {viewBounds: {left: 8, top: 16, right: 120, bottom: 32}, nextState: 'StateStart, declareSelf: 'Create, Action: func(answer) begin fTheAnswerToTheUltimateQuestionOfLifeTheUniverseAndEverything := answer; print(answer); end, debug: "Create", _proto: _userproto000 }; AddStepForm(Genesis, Create); State Start := {viewBounds: {left: 8, top: 64, right: 136, bottom: 128}, declareSelf: 'StateStart, debug: "State Start", _proto: _userproto001 }; AddStepForm(Example FSM, State Start); Event 1 Action 1 := {viewBounds: {left: 8, top: 16, right: 120, bottom: 32}, Action: func() begin :DoEvent('Event2, nil); end, nextState: 'State1, declareSelf: 'Event1, debug: "Event 1 Action 1", _proto: _userproto000 }; AddStepForm(State Start, Event 1 Action 1); Event 2 Action 3 := {viewBounds: {left: 8, top: 40, right: 120, bottom: 56}, Action: func() begin :DoEvent('Event1, nil); end, nextState: 'State2, declareSelf: 'Event2, debug: "Event 2 Action 3", _proto: _userproto000 }; AddStepForm(State Start, Event 2 Action 3); State 1 := {viewBounds: {left: 8, top: 136, right: 136, bottom: 176}, declareSelf: 'State1, debug: "State 1", _proto: _userproto001 }; AddStepForm(Example FSM, State 1); Event 2 Action 2 := {viewBounds: {left: 8, top: 16, right: 120, bottom: 32}, nextState: 'StateEnd, declareSelf: 'Event2, Action: func() begin // If there is nothing to do in an Action function, there's really no need to supply the slot. // See "State 2 / Event 1 Action 4" for an example of this. end, debug: "Event 2 Action 2", _proto: _userproto000 }; AddStepForm(State 1, Event 2 Action 2); State 2 := {viewBounds: {left: 8, top: 184, right: 136, bottom: 224}, declareSelf: 'State2, debug: "State 2", _proto: _userproto001 }; AddStepForm(Example FSM, State 2); Event 1 Action 4 := {viewBounds: {left: 8, top: 16, right: 120, bottom: 32}, nextState: 'StateEnd, declareSelf: 'Event1, debug: "Event 1 Action 4", _proto: _userproto000 }; AddStepForm(State 2, Event 1 Action 4); State End := {viewBounds: {left: 8, top: 232, right: 136, bottom: 256}, declareSelf: 'StateEnd, terminal: true, debug: "State End", _proto: _userproto001 }; AddStepForm(Example FSM, State End); // After Script for "Example FSM" thisView := Example FSM; call kFSMCleanUpFunc with (thisView); constant |layout_ExampleFSM| := Example FSM; // End of file ExampleFSM // Beginning of file Main.t // Before Script for "Example FSM" // Newton Developer Technical Support Sample Code // Maint.t - An NTK Finite State Machine Example Implementation // by Jim Schram, Newton Developer Technical Support // Copyright ©1996 Apple Computer, Inc. All rights reserved. // // You may incorporate this sample code into your applications without // restriction. This sample code has been provided "AS IS" and the // responsibility for its operation is 100% yours. You are not // permitted to modify and redistribute the source as "DTS Sample Code." // If you are going to re-distribute the source, we require that you // make it clear in the source that the code was descended from // Apple-provided sample code, but that you've made changes. Example FSM := {title: kAppName, viewBounds: {left: 0, top: 10, right: 219, bottom: 205}, viewFormat: 83952209, viewSetupDoneScript: func() begin :ResetFSM(); end, FSM: nil, viewQuitScript: // must return the value of inherited:?viewQuitScript(); func() begin FSM := FSM:Dispose(); inherited:?viewQuitScript(); // this method is defined internally end, ResetFSM: func() begin if FSM then FSM := FSM:Dispose(); FSM := GetLayout("ExampleFSM"):Instantiate(); FSM:DoEvent('Create, ["The answer to the ultimate question of life the universe and everything is 42."]); end, reorienttoscreen: ROM_DefRotateFunc, viewSetupFormScript: func() begin // resize to fit on all "small" newtons. constant kMaxWidth := 240; constant kMaxHeight := 336; local b := GetAppParams(); self.viewBounds := RelBounds(b.appAreaLeft, b.appAreaTop, MIN(b.appAreaWidth, kMaxWidth), MIN(b.appAreaHeight, kMaxHeight)); end, debug: "Example FSM", _proto: @157 }; Reset := { buttonClickScript: func() begin :ResetFSM(); end, text: "Reset", viewBounds: {left: 82, top: 26, right: 142, bottom: 46}, debug: "Reset", _proto: @226 }; AddStepForm(Example FSM, Reset); Event 1 := { buttonClickScript: func() begin if FSM:IsBusy() then GetRoot():Notify(kNotifyAlert, kAppName, "The state machine is busy. Please try again later."); else FSM:DoEvent('Event1, nil); end, text: "Event 1", viewBounds: {left: 34, top: 58, right: 102, bottom: 78}, debug: "Event 1", _proto: @226 }; AddStepForm(Example FSM, Event 1); Event 2 := { buttonClickScript: func() begin if FSM:IsBusy() then GetRoot():Notify(kNotifyAlert, kAppName, "The state machine is busy. Please try again later."); else FSM:DoEvent('Event2, nil); end, text: "Event 2", viewBounds: {left: 122, top: 58, right: 190, bottom: 78}, debug: "Event 2", _proto: @226 }; AddStepForm(Example FSM, Event 2); vTraceBox := {text: "", viewBounds: {left: 9, top: 89, right: 218, bottom: 170}, viewJustify: 0, viewFont: simpleFont9, debug: "vTraceBox", _proto: @218 }; AddStepForm(Example FSM, vTraceBox); StepDeclare(Example FSM, vTraceBox, 'vTraceBox); constant |layout_Main.t| := Example FSM; // End of file Main.t