// Text of project Thumb written on 4/25/97 at 5:40 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: -8, bottom: 32}, viewJustify: 8405040, _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: -8, bottom: 88}, viewJustify: 8396848, _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. // kFSMBuildTemplateFunc - compile time only // // Function to call at compile time to create a FSM from a text file definition or other non-layout source. // Performs basic sanity checking, constructs a protoFSM user template, and returns it. // // // DefConst ( 'kMyFSMTemplate, // call kFSMBuildTemplateFunc with ( 'myFSM, // symbol = required name of this FSM // nil, // frame or nil = optional context frame // Home & "myFSM.f", // string or frame = required name of frame definition file to load, or frame definition // GetLayout("protoFSM") // frame = required reference to protoFSM engine, or other engine // ) // ); // // // fsm := kMyFSMTemplate:Instantiate(); // kFSMBuildTemplateFunc := func(name, context, definition, engine) begin local fsmSymbol := name; if not fsmSymbol then begin fsmSymbol := 'unknown; print("A protoFSM implementation is unnamed (kFSMBuildTemplateFunc requires the name parameter)."); end; if not engine then begin print("The '|" & fsmSymbol & "| protoFSM engine is invalid."); return; end; definitionFrame := if IsInstance(definition, 'string) then Load(definition) else definition; if not IsInstance(definitionFrame, 'frame) then begin print("The '|" & fsmSymbol & "| protoFSM definition is invalid."); return; end; if not definitionFrame.Genesis then print("The '|" & fsmSymbol & "| protoFSM implementation is missing the required '|Genesis| state."); local templateFrame := if IsInstance(context, 'frame) then context else {}; templateFrame._proto := engine; templateFrame.declareSelf := fsmSymbol; templateFrame.fsm_private_states := definitionFrame; if not kDebugOn then // GoToState is a debug-only function! begin RemoveSlot(templateFrame._proto, 'GoToState); RemoveSlot(templateFrame, 'GoToState); end; RemoveSlot(templateFrame._proto, '_proto); RemoveSlot(templateFrame._proto, 'viewBounds); templateFrame; end; // kFSMCleanUpFunc - compile time only // // Function to call in the afterScript of your FSM layout. // Converts NTK layout data structures into "pure" FSM data structures used by protoFSM. // // Example: // // // call kFSMCleanUpFunc with (thisView); // let's assume the filename of this layout is "myFSM.t" // // // fsm := GetLayout("myFSM.t"):Instantiate(); // kFSMCleanUpFunc := func(fsmFrame) begin local fsmSymbol, stateSymbol, eventSymbol, hasGenesisState; RemoveSlot(fsmFrame._proto, '_proto); RemoveSlot(fsmFrame._proto, 'viewBounds); RemoveSlot(fsmFrame, 'viewBounds); RemoveSlot(fsmFrame, 'viewJustify); 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); RemoveSlot(stateFrame, 'viewJustify); 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); RemoveSlot(eventFrame, 'viewJustify); 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); nil; end _userproto002 := {viewBounds: {left: 8, top: 8, right: 232, bottom: 280}, DoEvent: func(eventSymbol, paramArray) // SELF can be anything that inherits to the finite state machine instance frame begin local x := :?DoEvent_Check('|protoFSM:DoEvent|); if not x then 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, isNewPendingState: true, 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 should be the finite state machine instance frame begin if not fsm_private_context then return; fsm_private_context.pendingEventQueue:Reset(); fsm_private_context.pendingParamsQueue:Reset(); foreach slot, value in fsm_private_context do fsm_private_context.(slot) := nil; fsm := currentStateFrame := currentEventFrame := fsm_private_context := nil; 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 repeat local x := :?DoEvent_Check('|protoFSM:DoEvent_Loop|); if not x then return; local ok := nil; local pendingStateFrame := nil; 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.isNewPendingState := true; 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(); if x.isNewPendingState then begin x.isNewPendingState := nil; currentStateFrame := { _proto: fsm_private_states.(x.currentState), _parent: self, }; end; 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 then begin x.pendingState := currentEventFrame.nextState; x.isNewPendingState := (x.currentState <> x.pendingState); end; 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; x.busy := not x.pendingEventQueue:IsEmpty(); until not(x.busy and currentEventFrame and currentEventFrame.nextNoIdle); if x.busy then 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 := ""; try exceptionStr := :ObjectToString(exception); onexception |evt.ex| do nil; :ExceptionHandler_Notify(message & exceptionStr); nil; end, ObjectToString: // Converts (any?) NewtonScript data type into a string representation. // Follows _proto pointers if the global var printProto is true. // Follows _parent pointers if the global var printParent is true. // Respects the global var printDepth (printDepth < 0 is effectively "infinite depth"). // Respects the global var printLength (printLength < 0 or nil is effectively "infinite length"). // Traps recursive references avoiding endless loops. // Gives each frame and array a "reference number" so backpointer references make sense. // Prints real numbers according to the global var printReal (a string defining the print format, e.g. "%.2f"). // Punts on any exception, like evt.ex.outofmem. // NOTE: This routine is primarily intended for use only during debugging. func(obj) begin constant separator := ", "; constant separatorLen := StrLen(separator); local backIndex := nil; local backList := [nil]; local runProto := GetGlobalVar('printProto) <> nil; local runParent := GetGlobalVar('printParent) <> nil; local floatFormat := GetGlobalVar('printReal); if not IsString(floatFormat) then floatFormat := "%.16e"; local maxDepth := GetGlobalVar('printDepth); if not IsNumber(maxDepth) or maxDepth < 0 then maxDepth := 65535; // arbitrarily large value local maxLength := GetGlobalVar('printLength); if not IsNumber(maxLength) or maxLength < 0 then maxLength := 65535; // arbitrarily large value local p := func native(s) begin if EndsWith(s, separator) then StrMunger(s, StrLen(s) - separatorLen, nil, nil, 0, nil) else s; end; local f := func native(obj, depth) begin local x := 0; ( if IsMagicPtr(obj) then "@+" & RefOf(obj) else if IsFunction(obj) then begin x := GetFunctionArgCount(obj); ParamStr("func(^0 arg^?1|s|)", [x, x = 1]); end else if IsFrame(obj) then if backIndex := LSearch(backList, obj, 1, '|=|, nil) then "<" & backIndex & ">" else begin local s := "{<" & Length(backList) & "> "; AddArraySlot(backList, obj); if depth > maxDepth then s := s & "+" & RefOf(obj); else foreach slot, item in obj do if (x := x + 1) > maxLength then begin s := s & "..."; break; end else s := s & slot & ": " & if (slot = '_parent and not runParent) or (slot = '_proto and not runProto) then "" & separator else call f with (item, depth + 1); call p with (s) & "}"; end else if IsArray(obj) then if backIndex := LSearch(backList, obj, 1, '|=|, nil) then "<" & backIndex & ">" else begin local s := "[<" & Length(backList) & "> " & (if ClassOf(obj) <> 'Array then ClassOf(obj) & ": "); AddArraySlot(backList, obj); if depth > maxDepth then s := s & "+" & RefOf(obj); else foreach item in obj do if (x := x + 1) > maxLength then begin s := s & "..."; break; end else s := s & call f with (item, depth + 1) ; call p with (s) & "]"; end else if IsString(obj) then $" & obj & $" else if IsSymbol(obj) then $' & obj else if IsInteger(obj) then (if obj > 0 then "+" else "") & NumberStr(obj) else if IsNumber(obj) then (if obj > 0 then "+" else "") & FormattedNumberStr(obj, floatFormat) else if IsImmediate(obj) then if not obj then "nil" else if obj = true then "true" else SPrintObject(obj) else if IsBinary(obj) then "<" & ClassOf(obj) & ", length " & Length(obj) & ">" else SPrintObject(obj) ) & separator; end; try call p with (call f with (obj, 0)); onexception |evt.ex.outofmem| do begin backList := nil; ""; end; onexception |evt.ex| do begin backList := nil; ""; end; end, DoEvent_Check: func(where) // This routine checks to see if the FSM has been unexpectedly disposed. That's especially bad if there's a delayed action/call/send about to trigger! begin local x := fsm_private_context; if x then return x; if kDebugOn then :?ExceptionHandler_Notify("An active protoFSM object has mysteriously vanished! Discovered in " & SPrintObject(where) & ". You should consider resetting your Newton device now."); nil; end, ExceptionHandler_Notify: func(message) begin GetRoot():Notify(kNotifyAlert, kAppName, message); if kDebugOn then begin print(message); if GetGlobalVar('BreakOnThrows) then BreakLoop(); end; nil; end, _proto: @218 }; constant |layout_protoFSM| := _userproto002; // End of file protoFSM // Beginning of file ThumbResults.t vFingerResults := {viewBounds: {left: -2, top: 18, right: 198, bottom: 218}, declareSelf: 'base, MMessage: func(message) begin SetValue(vText, 'text, message); end, debug: "vFingerResults", _proto: @179 }; vText := {text: "", viewBounds: {left: 2, top: 2, right: -2, bottom: -40}, viewFont: simpleFont9, viewJustify: 240, debug: "vText", _proto: @218 }; AddStepForm(vFingerResults, vText); StepDeclare(vFingerResults, vText, 'vText); _view000 := { buttonClickScript: func() begin local notes := GetRoot().paperroll; if notes and call kViewIsOpenFunc with (notes) then notes:MakeTextNote(vText.text, true); else GetRoot():Notify(kNotifyAlert, kAppName, "Sorry, the notes application is not active."); end, text: "Copy To Notepad", viewBounds: {left: -126, top: -18, right: -26, bottom: -5}, viewJustify: 8388774, _proto: @226 }; AddStepForm(vFingerResults, _view000); _view001 := {_proto: @163}; AddStepForm(vFingerResults, _view001); constant |layout_ThumbResults.t| := vFingerResults; // End of file ThumbResults.t // Beginning of file ThumbFSM // Before Script for "Finger Machine" // Newton Developer Technical Support Sample Code // ThumbFSM - An NTK Finite State Machine 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. // Eventually the following global functions will be defined... for now let's tell NTK to be quiet about their use. DeclareGlobalFn('DeclareGlobalFn, 2); DeclareGlobalFn('InetOpenConnectionSlip, 3); DeclareGlobalFn('InetDisplayStatus, 3); DeclareGlobalFn('InetGetErrorString, 1); DeclareGlobalFn('InetGrabLink, 3); DeclareGlobalFn('InetReleaseLink, 3); DeclareGlobalFn('InetCancelLink, 3); DeclareGlobalFn('DNSGetAddressFromName, 3); DeclareGlobalFn('DNSCancelRequests, 2); Finger Machine := { DebugFSM: 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, fLinkID: nil, fFingerResponse: nil, MShowFingerResults: func(message) begin local view := BuildContext(GetLayout("ThumbResults.t")); view:Open(); view:MMessage(message); end, fRemoteIPAddressStr: nil, fProtocol: nil, fState: nil, fRemoteIPAddress: nil, viewBounds: {left: 0, top: 0, right: 192, bottom: 2000}, _proto: _userproto002, MTrace: func(s) // Assumes an open and visible protoStaticText view in the application's base view called vTraceBox. begin local base := GetRoot().(kAppSymbol); if base and call kViewIsOpenFunc with (base) and base.vTraceBox and call kViewIsOpenFunc with (base.vTraceBox) then begin SetValue(base.vTraceBox, 'text, s); RefreshViews(); end; end, MNotifyError: // where = string describing location of error // error = NewtonScript object (e.g. integer, real number, exception frame, etc.) describing the error func(where, error) begin if not error or error = -16005 or fAbort then return; local message := "An error occured in " & where & ". "; if IsNumber(error) and GetGlobalFn('InetGetErrorString) then // NIE 1.1 message := message & InetGetErrorString(error) & " "; message := message & "Error = " & :ObjectToString(error); :MNotify(message); end, fAbort: nil, fEndpoint: nil, fRemoteHostName: nil, fLinkStatusView: nil, declareSelf: 'FingerMachine, fFingerRequest: nil, fRemotePortNumber: nil, debug: "Finger Machine", fLocalPortNumber: nil, fPowerOffState: nil, MNotify: func(message) begin GetRoot():Notify(kNotifyAlert, kAppName, message); end, TraceFSM: 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 }; Genesis := {terminal: true, declareSelf: 'Genesis, viewBounds: {left: 8, top: 16, right: -8, bottom: 56}, debug: "Genesis", _proto: _userproto001 }; AddStepForm(Finger Machine, Genesis); Create := { action: func(hostStr, fingerStr) begin fsm.fFingerRequest := Clone(fingerStr); fsm.fFingerResponse := ""; fsm.fState := 'Connecting; fsm.fPowerOffState := nil; fsm.fAbort := nil; fsm.fConnectionSlipView := nil; fsm.fGrabLinkReceiver := nil; fsm.fReleaseLinkReceiver := nil; fsm.fDNSReceiver := nil; fsm.fRemoteHostName:= Clone(hostStr); fsm.fRemoteIPAddress := nil; fsm.fRemoteIPAddressStr := ""; fsm.fRemotePortNumber := 79; // 79 = finger port fsm.fLocalPortNumber := 0; // ephemeral port fsm.fProtocol := 1; // 1 = TCP, 2 = UDP fsm.fLinkID := nil; fsm.fLinkStatusView := nil; fsm.fEndpoint := { _proto: protoBasicEndpoint, _parent: fsm, }; RegPowerOff( kAppSymbol, func(what, why) // we create the closure here so as to set up SELF as the state machine frame in the closure begin if what = 'okToPowerOff then begin if why <> 'idle // keep the unit awake whenever we're connected or fsm.fState = 'Disconnected then // unless the user or an application explicitly return true; // wants it to sleep end; else if what = 'powerOff then begin if why <> 'idle // if we simply must go to sleep but we're still or fsm.fState <> 'Disconnected then // connected then begin the disconnect process begin fsm.fPowerOffState := 'holdYourHorses; // set a flag to indicate we're powering down :DoEvent('Cancel, nil); return 'holdYourHorses; end; end; nil; // ALWAYS return nil here! end ); if GetGlobalFn('InetGrabLink) then :DoEvent('OpenConnectionSlip, nil); else :DoEvent('Abort, nil); end, nextState: 'OpenConnectionSlip, declareSelf: 'Create, nextNoIdle: true, debug: "Create", _proto: _userproto000 }; AddStepForm(Genesis, Create); Open Connection Slip := {declareSelf: 'OpenConnectionSlip, viewBounds: {left: 8, top: 16, right: -8, bottom: 120}, debug: "Open Connection Slip", _proto: _userproto001 }; AddStepForm(Finger Machine, Open Connection Slip); Open Connection Slip := {declareSelf: 'OpenConnectionSlip, action: func() begin fConnectionSlipView := InetOpenConnectionSlip(fLinkID, self, 'MConnectionSlipCallback); end, MConnectionSlipCallback: func(what) begin if what = 'Connect then :DoEvent('OpenConnectionConnect, nil); else :DoEvent('OpenConnectionClose, nil); end, debug: "Open Connection Slip", _proto: _userproto000 }; AddStepForm(Open Connection Slip, Open Connection Slip); Open Connection Connect := {declareSelf: 'OpenConnectionConnect, action: func() begin fLinkStatusView := InetDisplayStatus(fLinkID, nil, nil); :DoEvent('OpenLink, nil); end, nextState: 'OpenLink, debug: "Open Connection Connect", _proto: _userproto000 }; AddStepForm(Open Connection Slip, Open Connection Connect); Open Connection Close := {declareSelf: 'OpenConnectionClose, action: func() begin :DoEvent('CleanUp, nil); end, nextState: 'CleanUp, debug: "Open Connection Close", _proto: _userproto000 }; AddStepForm(Open Connection Slip, Open Connection Close); Abort := {declareSelf: 'Abort, action: func() begin :MNotify("The Newton Internet Enabler is not installed."); :DoEvent('CleanUp, nil); end, nextState: 'CleanUp, debug: "Abort", _proto: _userproto000 }; AddStepForm(Open Connection Slip, Abort); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; if fConnectionSlipView then fConnectionSlipView:Close(); :DoEvent('CleanUp, nil); end, nextState: 'CleanUp, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Open Connection Slip, Cancel); Open Link := {declareSelf: 'OpenLink, viewBounds: {left: 8, top: 16, right: -8, bottom: 136}, debug: "Open Link", _proto: _userproto001 }; AddStepForm(Finger Machine, Open Link); Open Link := {declareSelf: 'OpenLink, action: func() begin InetDisplayStatus(fLinkID, fLinkStatusView, { statusText: "Opening link..." } ); fGrabLinkReceiver := self; // remember this in case we need to call InetCancelLink later on... InetGrabLink(fLinkID, fGrabLinkReceiver, 'MGrabLinkCallback); end, MGrabLinkCallback: func(linkID, newStatus, error) begin fLinkID := linkID; // make sure it's set... InetDisplayStatus(fLinkID, fLinkStatusView, newStatus); if error then return :DoEvent('OpenLinkFailure, [error]); if newStatus.linkStatus = 'Connected then :DoEvent('OpenLinkSuccess, nil); end, MCancelLinkCallback: func(linkID, newStatus, error) begin InetDisplayStatus(fLinkID, fLinkStatusView, newStatus); if error then :DoEvent('CancelLinkFailure, [error]); else :DoEvent('CancelLinkSuccess, nil); end, debug: "Open Link", _proto: _userproto000 }; AddStepForm(Open Link, Open Link); Open Link Success := {nextState: 'GetAddress, declareSelf: 'OpenLinkSuccess, action: func() begin fGrabLinkReceiver := nil; if fAbort then return :DoEvent('Abort, nil); InetDisplayStatus(fLinkID, fLinkStatusView, { statusText: "Looking up host...", titleText: fRemoteHostName } ); :DoEvent('GetAddress, nil); end, nextNoIdle: true, debug: "Open Link Success", _proto: _userproto000 }; AddStepForm(Open Link, Open Link Success); Open Link Failure := {nextState: 'CleanUp, declareSelf: 'OpenLinkFailure, action: func(error) begin fGrabLinkReceiver := nil; :MNotifyError("Open Link", error); :DoEvent('CleanUp, nil); end, debug: "Open Link Failure", _proto: _userproto000 }; AddStepForm(Open Link, Open Link Failure); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; fLinkStatus := { statusText: "Cancelling Open Link Request..."}; InetDisplayStatus(fLinkID, fLinkStatusView, fLinkStatus); InetCancelLink(fLinkID, fGrabLinkReceiver, 'MCancelLinkCallback); end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Open Link, Cancel); Cancel Link Success := {declareSelf: 'CancelLinkSuccess, debug: "Cancel Link Success", _proto: _userproto000 }; AddStepForm(Open Link, Cancel Link Success); Cancel Link Failure := {declareSelf: 'CancelLinkFailure, action: func(error) begin :MNotifyError("Cancel Open Link", error); end, debug: "Cancel Link Failure", _proto: _userproto000 }; AddStepForm(Open Link, Cancel Link Failure); Get Address := {declareSelf: 'GetAddress, viewBounds: {left: 8, top: 16, right: -8, bottom: 120}, debug: "Get Address", _proto: _userproto001 }; AddStepForm(Finger Machine, Get Address); Get Address := {declareSelf: 'GetAddress, action: func() begin fDNSReceiver := self; // remember this in case we need to call DNSCancelRequests later on... DNSGetAddressFromName(fRemoteHostName, fDNSReceiver, 'MGetAddressCallback); end, MGetAddressCallback: func(results, error) begin if error or not results or Length(results) < 1 then return :DoEvent('GetAddressFailure, [error]); fRemoteIPAddress := results[0].resultIPAddress; fRemoteIPAddressStr := "IP Address: " & :MIPAddressToString(fRemoteIPAddress); :DoEvent('GetAddressSuccess, nil); end, MIPAddressToString: func(address) // Convert a 4 byte array to a "w.x.y.z" string. begin if address and Length(address) = 4 then NumberStr(address[0]) & "." & NumberStr(address[1]) & "." & NumberStr(address[2]) & "." & NumberStr(address[3]) else ""; end, MCancelDNSCallback: func() begin :DoEvent('GetAddressFailure, [-666]); end, debug: "Get Address", _proto: _userproto000 }; AddStepForm(Get Address, Get Address); Get Address Success := {nextState: 'Instantiate, declareSelf: 'GetAddressSuccess, action: func() begin fDNSReceiver := nil; if fAbort then return :DoEvent('Abort, nil); fLinkStatus := { statusText: "Instantiating...", titleText: fRemoteIPAddressStr, }; InetDisplayStatus(fLinkID, fLinkStatusView, { statusText: "Instantiating...", titleText: fRemoteIPAddressStr } ); :DoEvent('Instantiate, nil); end, debug: "Get Address Success", _proto: _userproto000 }; AddStepForm(Get Address, Get Address Success); Get Address Failure := {nextState: 'CloseLink, declareSelf: 'GetAddressFailure, action: func(error) begin fDNSReceiver := nil; :MNotifyError("Get DNS Address", error); :DoEvent('CloseLink, nil); end, debug: "Get Address Failure", _proto: _userproto000 }; AddStepForm(Get Address, Get Address Failure); Abort := {declareSelf: 'Abort, action: func() begin :DoEvent('CloseLink, nil); end, nextState: 'CloseLink, debug: "Abort", _proto: _userproto000 }; AddStepForm(Get Address, Abort); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; if fDNSReceiver then DNSCancelRequests(fDNSReceiver, 'MCancelDNSCallback); end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Get Address, Cancel); Instantiate := {declareSelf: 'Instantiate, debug: "Instantiate", _proto: _userproto001}; AddStepForm(Finger Machine, Instantiate); Instantiate := { action: func() begin try fEndPoint:Instantiate(fEndPoint, :MBuildConfigOptions(fLinkID, fProtocol)); onexception |evt.ex| do return :DoEvent('InstantiateFailure, [CurrentException()]); :DoEvent('InstantiateSuccess, nil); end, declareSelf: 'Instantiate, MBuildConfigOptions: func(linkID, protocol) begin [ { label: "inet", type: 'service, opCode: opSetRequired, result: nil, }, { label: "ilid", // set the link id type: 'option, opCode: opSetRequired, result: nil, form: 'template, data: { arglist: [ linkID, ], typelist: [ 'struct, 'ulong, ], }, }, { label: "itsv", // set the transport protocol (TCP or UCP) type: 'option, opCode: opSetRequired, result: nil, form: 'template, data: { arglist: [ protocol, ], typelist: [ 'struct, 'ulong, ], }, }, ]; end, nextState: 'Instantiating, debug: "Instantiate", _proto: _userproto000 }; AddStepForm(Instantiate, Instantiate); Abort := {declareSelf: 'Abort, action: func() begin :DoEvent('CloseLink, nil); end, nextState: 'CloseLink, debug: "Abort", _proto: _userproto000 }; AddStepForm(Instantiate, Abort); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Instantiate, Cancel); Instantiating := {declareSelf: 'Instantiating, debug: "Instantiating", _proto: _userproto001} ; AddStepForm(Finger Machine, Instantiating); Instantiate Success := { action: func() begin if fAbort then return :DoEvent('Abort, nil); :DoEvent('Bind, nil); end, declareSelf: 'InstantiateSuccess, nextState: 'Bind, debug: "Instantiate Success", _proto: _userproto000 }; AddStepForm(Instantiating, Instantiate Success); Instantiate Failure := { action: func(error) begin :MNotifyError("Endpoint Instantiate", error); :DoEvent('CloseLink, nil); end, declareSelf: 'InstantiateFailure, nextState: 'CloseLink, debug: "Instantiate Failure", _proto: _userproto000 }; AddStepForm(Instantiating, Instantiate Failure); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Instantiating, Cancel); Bind := {declareSelf: 'Bind, debug: "Bind", _proto: _userproto001}; AddStepForm(Finger Machine, Bind); Bind := {declareSelf: 'Bind, action: func() begin InetDisplayStatus(fLinkID, fLinkStatusView, { statusText: "Binding to port..." } ); try fEndPoint:Bind( :MBuildConfigOptions(fLocalPortNumber, true), fCompletionSpec); onexception |evt.ex| do :DoEvent('BindFailure, [CurrentException()]); end, fCompletionSpec: { async: true, reqTimeout: kNoTimeout, completionScript: func(ep, options, result) begin if result then ep:DoEvent('BindFailure, [result]); else ep:DoEvent('BindSuccess, nil); end, }, MBuildConfigOptions: func(localPort, useDefaultPort) begin [ { label: "ilpt", // set the local port type: 'option, opCode: opSetRequired, result: nil, form: 'template, data: { arglist: [ localPort, // local port number useDefaultPort, ], // use default port typelist: [ 'struct, 'short, 'boolean, ], }, }, ]; end, nextState: 'Binding, debug: "Bind", _proto: _userproto000 }; AddStepForm(Bind, Bind); Abort := {declareSelf: 'Abort, action: func() begin :DoEvent('Dispose, nil); end, nextState: 'Dispose, debug: "Abort", _proto: _userproto000 }; AddStepForm(Bind, Abort); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Bind, Cancel); Binding := {declareSelf: 'Binding, debug: "Binding", _proto: _userproto001}; AddStepForm(Finger Machine, Binding); Bind Success := {declareSelf: 'BindSuccess, action: func() begin if fAbort then return :DoEvent('Abort, nil); :DoEvent('Connect, nil); end, nextState: 'Connect, debug: "Bind Success", _proto: _userproto000 }; AddStepForm(Binding, Bind Success); Bind Failure := {declareSelf: 'BindFailure, action: func(error) begin :MNotifyError("Endpoint Bind", error); :DoEvent('Dispose, nil); end, nextState: 'Dispose, debug: "Bind Failure", _proto: _userproto000 }; AddStepForm(Binding, Bind Failure); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; try fEndPoint:Cancel(nil); onexception |evt.ex.comm| do nil; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Binding, Cancel); Connect := {declareSelf: 'Connect, debug: "Connect", _proto: _userproto001}; AddStepForm(Finger Machine, Connect); Connect := {declareSelf: 'Connect, action: func() begin InetDisplayStatus(fLinkID, fLinkStatusView, { statusText: "Connecting to port..." } ); try fEndPoint:Connect(:MBuildConnectConfigOptions(fRemoteIPAddress, fRemotePortNumber), fCompletionSpec_Connect); onexception |evt.ex| do :DoEvent('ConnectFailure, [CurrentException()]); end, fCompletionSpec_Connect: { async: true, reqTimeout: kNoTimeout, completionScript: func(ep, options, result) begin if result then ep:DoEvent('ConnectFailure, [result]); else ep:DoEvent('ConnectSuccess, nil); end, }, MBuildConnectConfigOptions: func(remoteAddr, remotePort) begin [ { label: "itrs", // set the TCP remote socket type: 'option, opCode: opSetRequired, result: nil, form: 'template, data: { arglist: [ remoteAddr[0], // remote host addr - byte 1 remoteAddr[1], // remote host addr - byte 2 remoteAddr[2], // remote host addr - byte 3 remoteAddr[3], // remote host addr - byte 4 remotePort, ], // remote port number typelist: [ 'struct, 'byte, 'byte, 'byte, 'byte, 'short, ], }, }, ]; end, nextState: 'Connecting, debug: "Connect", _proto: _userproto000 }; AddStepForm(Connect, Connect); Abort := {declareSelf: 'Abort, action: func() begin :DoEvent('UnBind, nil); end, nextState: 'UnBind, debug: "Abort", _proto: _userproto000 }; AddStepForm(Connect, Abort); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Connect, Cancel); Connecting := {declareSelf: 'Connecting, debug: "Connecting", _proto: _userproto001}; AddStepForm(Finger Machine, Connecting); Connect Success := { action: func() begin fState := 'Connected; InetDisplayStatus(fLinkID, fLinkStatusView, { statusText: "Connected." } ); :DoEvent('Connected, nil); end, declareSelf: 'ConnectSuccess, nextState: 'Connected, debug: "Connect Success", _proto: _userproto000 }; AddStepForm(Connecting, Connect Success); Connect Failure := { action: func(error) begin :MNotifyError("Endpoint Connect", error); :DoEvent('UnBind, nil); end, declareSelf: 'ConnectFailure, nextState: 'UnBind, debug: "Connect Failure", _proto: _userproto000 }; AddStepForm(Connecting, Connect Failure); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; try fEndPoint:Cancel(nil); onexception |evt.ex.comm| do nil; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Connecting, Cancel); Connected := {declareSelf: 'Connected, viewBounds: {left: 8, top: 16, right: -8, bottom: 104}, debug: "Connected", _proto: _userproto001 }; AddStepForm(Finger Machine, Connected); Connected := {declareSelf: 'Connected, action: func() begin fLinkStatusView := InetDisplayStatus(fLinkID, fLinkStatusView, nil); // Close the status view if fAbort then return :DoEvent('Disconnect, nil); :DoEvent('SendFingerRequest, [fFingerRequest]); end, debug: "Connected", _proto: _userproto000 }; AddStepForm(Connected, Connected); Disconnect := { action: func() begin fState := 'Disconnecting; // The finger protocol specifies the host will probably already be disconnected // by the time we get around to disconnecting our end. So we handle this // situation by acting like we're aborting during disconnect (i.e. don't report // the "already-disconnected, out-of-state" error to the user). fAbort := true; fLinkStatusView := InetDisplayStatus(fLinkID, nil, nil); InetDisplayStatus(fLinkID, fLinkStatusView, { statusText: "Disconnecting..." } ); try fEndPoint:Disconnect(true, fCompletionSpec); onexception |evt.ex| do :DoEvent('DisconnectFailure, [CurrentException()]); end, declareSelf: 'Disconnect, nextState: 'Disconnecting, fCompletionSpec: { async: true, reqTimeout: kNoTimeout, completionScript: func(ep, options, result) begin if result then ep:DoEvent('DisconnectFailure, [result]); else ep:DoEvent('DisconnectSuccess, nil); end, }, debug: "Disconnect", _proto: _userproto000 }; AddStepForm(Connected, Disconnect); Cancel := {declareSelf: 'Cancel, action: func() begin if fPowerOffState then :DoEvent('Disconnect, nil); else try fEndPoint:Cancel(nil); onexception |evt.ex.comm| do nil; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Connected, Cancel); Send Finger Request := { action: func(data) begin :DoEvent('Send, [data]); end, declareSelf: 'SendFingerRequest, nextState: 'SendFingerRequest, nextNoIdle: true, debug: "Send Finger Request", _proto: _userproto000 }; AddStepForm(Connected, Send Finger Request); Disconnecting := {declareSelf: 'Disconnecting, debug: "Disconnecting", _proto: _userproto001} ; AddStepForm(Finger Machine, Disconnecting); Disconnect Success := { action: func() begin :DoEvent('UnBind, nil); end, declareSelf: 'DisconnectSuccess, nextState: 'UnBind, debug: "Disconnect Success", _proto: _userproto000 }; AddStepForm(Disconnecting, Disconnect Success); Disconnect Failure := { action: func(error) begin :MNotifyError("Endpoint Disconnect", error); :DoEvent('UnBind, nil); end, declareSelf: 'DisconnectFailure, nextState: 'UnBind, debug: "Disconnect Failure", _proto: _userproto000 }; AddStepForm(Disconnecting, Disconnect Failure); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; try fEndPoint:Cancel(nil); onexception |evt.ex.comm| do nil; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Disconnecting, Cancel); UnBind := {declareSelf: 'UnBind, viewBounds: {left: 8, top: 16, right: -8, bottom: 72}, debug: "UnBind", _proto: _userproto001 }; AddStepForm(Finger Machine, UnBind); UnBind := {declareSelf: 'UnBind, action: func() begin InetDisplayStatus(fLinkID, fLinkStatusView, { statusText: "UnBinding..." } ); try fEndPoint:UnBind(fCompletionSpec); onexception |evt.ex| do :DoEvent('UnBindFailure, [CurrentException()]); end, fCompletionSpec: { async: true, reqTimeout: kNoTimeout, completionScript: func(ep, options, result) begin if result then ep:DoEvent('UnBindFailure, [result]); else ep:DoEvent('UnBindSuccess, nil); end, }, nextState: 'UnBinding, debug: "UnBind", _proto: _userproto000 }; AddStepForm(UnBind, UnBind); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(UnBind, Cancel); UnBinding := {declareSelf: 'UnBinding, debug: "UnBinding", _proto: _userproto001}; AddStepForm(Finger Machine, UnBinding); UnBind Success := { action: func() begin :DoEvent('Dispose, nil); end, declareSelf: 'UnBindSuccess, nextState: 'Dispose, debug: "UnBind Success", _proto: _userproto000 }; AddStepForm(UnBinding, UnBind Success); UnBind Failure := { action: func(exception) begin :MNotifyError("Endpoint UnBind", exception); :DoEvent('Dispose, nil); end, declareSelf: 'UnBindFailure, nextState: 'Dispose, debug: "UnBind Failure", _proto: _userproto000 }; AddStepForm(UnBinding, UnBind Failure); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; try fEndPoint:Cancel(nil); onexception |evt.ex.comm| do nil; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(UnBinding, Cancel); Dispose := {declareSelf: 'Dispose, viewBounds: {left: 8, top: 16, right: -8, bottom: 72}, debug: "Dispose", _proto: _userproto001 }; AddStepForm(Finger Machine, Dispose); Dispose := { action: func() begin try fEndPoint:Dispose(); onexception |evt.ex| do return :DoEvent('DisposeFailure, [CurrentException()]); :DoEvent('DisposeSuccess, nil); end, declareSelf: 'Dispose, nextState: 'Disposing, debug: "Dispose", _proto: _userproto000 }; AddStepForm(Dispose, Dispose); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Dispose, Cancel); Disposing := {declareSelf: 'Disposing, debug: "Disposing", _proto: _userproto001}; AddStepForm(Finger Machine, Disposing); Dispose Success := { action: func() begin :DoEvent('CloseLink, nil); end, declareSelf: 'DisposeSuccess, nextState: 'CloseLink, debug: "Dispose Success", _proto: _userproto000 }; AddStepForm(Disposing, Dispose Success); Dispose Failure := { action: func(error) begin :MNotifyError("Endpoint Dispose", error); :DoEvent('CloseLink, nil); end, declareSelf: 'DisposeFailure, nextState: 'CloseLink, debug: "Dispose Failure", _proto: _userproto000 }; AddStepForm(Disposing, Dispose Failure); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Disposing, Cancel); Close Link := {declareSelf: 'CloseLink, viewBounds: {left: 8, top: 16, right: -8, bottom: 136}, debug: "Close Link" , _proto: _userproto001 }; AddStepForm(Finger Machine, Close Link); Close Link := {declareSelf: 'CloseLink, action: func() begin InetDisplayStatus(fLinkID, fLinkStatusView, { statusText: "Closing link..." } ); fReleaseLinkReceiver := self; // remember this in case we need to call InetCancelLink later on... InetReleaseLink(fLinkID, fReleaseLinkReceiver, 'MReleaseLinkCallback); end, MReleaseLinkCallback: func(linkID, newStatus, error) begin InetDisplayStatus(fLinkID, fLinkStatusView, newStatus); if error then :DoEvent('CloseLinkFailure, [error]); else :DoEvent('CloseLinkSuccess, nil); end, MCancelLinkCallback: func(linkID, newStatus, error) begin InetDisplayStatus(fLinkID, fLinkStatusView, newStatus); if error then :DoEvent('CancelLinkFailure, [error]); else :DoEvent('CancelLinkSuccess, nil); end, debug: "Close Link", _proto: _userproto000 }; AddStepForm(Close Link, Close Link); Close Link Success := {nextState: 'CleanUp, declareSelf: 'CloseLinkSuccess, action: func() begin fReleaseLinkReceiver := nil; :DoEvent('CleanUp, nil); end, debug: "Close Link Success", _proto: _userproto000 }; AddStepForm(Close Link, Close Link Success); Close Link Failure := {nextState: 'CleanUp, declareSelf: 'CloseLinkFailure, action: func(error) begin fReleaseLinkReceiver := nil; :MNotifyError("Close Link", error); :DoEvent('CleanUp, nil); end, debug: "Close Link Failure", _proto: _userproto000 }; AddStepForm(Close Link, Close Link Failure); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; InetDisplayStatus(fLinkID, fLinkStatusView, { statusText: "Cancelling Close Link Request...", titleText: "" } ); InetCancelLink(fLinkID, fReleaseLinkReceiver, 'MCancelLinkCallback); end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Close Link, Cancel); Cancel Link Success := {declareSelf: 'CancelLinkSuccess, debug: "Cancel Link Success", _proto: _userproto000 }; AddStepForm(Close Link, Cancel Link Success); Cancel Link Failure := {declareSelf: 'CancelLinkFailure, action: func(error) begin :MNotifyError("Cancel Close Link", error); end, debug: "Cancel Link Failure", _proto: _userproto000 }; AddStepForm(Close Link, Cancel Link Failure); Clean Up := {declareSelf: 'CleanUp, viewBounds: {left: 8, top: 16, right: -8, bottom: 56}, debug: "Clean Up", _proto: _userproto001 }; AddStepForm(Finger Machine, Clean Up); Clean Up := { action: func() begin if fPowerOffState then begin fPowerOffState := nil; PowerOffResume(kAppSymbol); end; UnRegPowerOff(kAppSymbol); if fLinkStatusView then fLinkStatusView := InetDisplayStatus(fLinkID, fLinkStatusView, nil); fLinkID := nil; fProtocol := nil; fLocalPortNumber := nil; fRemotePortNumber := nil; fRemoteIPAddress := nil; fRemoteHostName:= nil; fDNSReceiver := nil; fReleaseLinkReceiver := nil; fGrabLinkReceiver := nil; fConnectionSlipView := nil; fState := 'Disconnected; fFingerResponse := nil; fFingerRequest := nil; end, declareSelf: 'CleanUp, nextState: 'Genesis, debug: "Clean Up", _proto: _userproto000 }; AddStepForm(Clean Up, Clean Up); Send Finger Request := {declareSelf: 'SendFingerRequest, viewBounds: {left: 8, top: 16, right: -8, bottom: 136}, debug: "Send Finger Request", _proto: _userproto001 }; AddStepForm(Finger Machine, Send Finger Request); Send := { action: func(data) begin local spec := { _proto: fInputSpec, _parent: fsm, }; fEndPoint:SetInputSpec(spec); fEndPoint:Output(data, nil, fCompletionSpec); end, declareSelf: 'Send, fCompletionSpec: { async: true, reqTimeout: kNoTimeout, form: 'string, completionScript: func(ep, options, result) begin if result then ep:DoEvent('SendFailure, [result]); else ep:DoEvent('SendTerminator, nil); end, }, fInputSpec: { async: true, reqTimeout: 30000, // timeout in 30 seconds discardAfter: 2048, form: 'string, filter: { sevenBit: true, byteProxy: [ { byte: unicodeLF, proxy: nil, }, ], }, termination: { endSequence: unicodeCR, }, inputScript: func(ep, data, terminator, options) begin if kDebugOn then PlaySoundSync(ROM_Click); if StrFilled(data) then fFingerResponse := fFingerResponse & data; // parent of this inputSpec is the fsm frame end, completionScript: func(ep, options, result) begin if kDebugOn then PlaySoundSync(ROM_FunBeep); ep:DoEvent('ReceiveFailure, [result]); end, }, debug: "Send", _proto: _userproto000 }; AddStepForm(Send Finger Request, Send); Send Terminator := { action: func() begin fEndPoint:Output(unicodeCR & unicodeLF, nil, fCompletionSpec); end, declareSelf: 'SendTerminator, fCompletionSpec: { async: true, reqTimeout: kNoTimeout, form: 'string, completionScript: func(ep, options, result) begin if result then ep:DoEvent('SendFailure, [result]); end, }, debug: "Send Terminator", _proto: _userproto000 }; AddStepForm(Send Finger Request, Send Terminator); Send Failure := { action: func(error) begin :MNotifyError("Send Finger Request", error); :DoEvent('Cancel, nil); end, declareSelf: 'SendFailure, debug: "Send Failure", _proto: _userproto000 }; AddStepForm(Send Finger Request, Send Failure); Receive Failure := {declareSelf: 'ReceiveFailure, nextState: 'Connected, action: func(error) begin if error <> -16005 // user canceled and StrFilled(fFingerResponse) then :MShowFingerResults(fFingerResponse); // NOTE: The finger protocol specifies the connection will be torn down // after the last byte of data has been sent. NIE detects this, causing the // inputSpec's completionSpec to be called accordingly (that's how we know // there is no more finger text to be displayed). However, the error code // returned was incorrect in NIE 1.0 and has been fixed in NIE 1.1 as follows: if error <> -60008 // Get request failure (NIE 1.0) and error <> -16009 then // Endpoint disconnected (NIE 1.1) :MNotifyError("Receive Finger Request", error); :DoEvent('Disconnect, nil); end, debug: "Receive Failure", _proto: _userproto000 }; AddStepForm(Send Finger Request, Receive Failure); Cancel := {declareSelf: 'Cancel, action: func() begin fAbort := true; try fEndPoint:Cancel(nil); onexception |evt.ex.comm| do nil; :DoEvent('Disconnect, nil); end, debug: "Cancel", _proto: _userproto000 }; AddStepForm(Send Finger Request, Cancel); Disconnect := {declareSelf: 'Disconnect, action: func() begin :DoEvent('Cancel, nil); end, debug: "Disconnect", _proto: _userproto000 }; AddStepForm(Send Finger Request, Disconnect); // After Script for "Finger Machine" thisView := Finger Machine; call kFSMCleanUpFunc with (thisView); constant |layout_ThumbFSM| := Finger Machine; // End of file ThumbFSM // Beginning of file Main.t vMain := {title: "Thumb (Finger) FSM Example", viewBounds: {left: -4, top: 2, right: 224, bottom: 310}, viewFormat: 83951953, viewSetupDoneScript: func() begin :ResetFSM(); end, FSM: nil, viewQuitScript: // must return the value of inherited:?viewQuitScript(); func() begin FSM:DoEvent('Cancel, nil); FSM:DoEvent('Disconnect, nil); FSM:WaitForTerminal( // this function returns NIL if FSM terminal state reached normally, TRUE if user aborted { statusText: "Please wait...", // message at top of status dialog titleText: "FSM is executing toward terminal state.", // message at bottom of status dialog delayUntilStatusVisible: 2000, // show status slip 2 seconds after entering WaitForTerminal delayUntilAbortVisible: 8000, // show abort button 8 seconds after status dialog opens abortButtonText: "Go Away!", } ); // text inside abort button FSM := FSM:Dispose(); inherited:?viewQuitScript(); // this method is defined internally end, ResetFSM: func() begin if FSM then FSM := FSM:Dispose(); FSM := GetLayout("ThumbFSM"):Instantiate(); end, viewSetupFormScript: func() begin // resize to fit on all "small" newtons. local b := GetAppParams(); constant kMaxWidth := 240; constant kMaxHeight := 336; self.viewBounds := RelBounds(b.appAreaLeft, b.appAreaTop, MIN(b.appAreaWidth, kMaxWidth), MIN(b.appAreaHeight, kMaxHeight)); end, ReorientToScreen: ROM_DefRotateFunc, debug: "vMain", _proto: @157 }; Reset := { buttonClickScript: func() begin :ResetFSM(); end, text: "Reset", viewBounds: {left: 86, top: 42, right: 154, bottom: 62}, debug: "Reset", _proto: @226 }; AddStepForm(vMain, Reset); Connect := { buttonClickScript: func() begin FSM:DoEvent('Create, [vRemoteText.text, vRequestText.text]); end, text: "Connect", viewBounds: {left: 46, top: 74, right: 114, bottom: 94}, debug: "Connect", _proto: @226 }; AddStepForm(vMain, Connect); Disconnect := { buttonClickScript: func() begin FSM:DoEvent('Disconnect, nil); end, text: "Disconnect", viewBounds: {left: 126, top: 74, right: 194, bottom: 94}, debug: "Disconnect", _proto: @226 }; AddStepForm(vMain, Disconnect); Cancel := { buttonClickScript: func() begin FSM:DoEvent('Cancel, nil); end, text: "Cancel", viewBounds: {left: 86, top: 106, right: 154, bottom: 126}, debug: "Cancel", _proto: @226 }; AddStepForm(vMain, Cancel); vTraceBox := {text: "", viewBounds: {left: 8, top: 192, right: 224, bottom: 264}, viewJustify: 0, viewFont: simpleFont9, debug: "vTraceBox", _proto: @218 }; AddStepForm(vMain, vTraceBox); StepDeclare(vMain, vTraceBox, 'vTraceBox); _view002 := {text: "Host name:", viewBounds: {left: 8, top: 140, right: 68, bottom: 156}, viewJustify: 8388609, _proto: @218 }; AddStepForm(vMain, _view002); vRemoteText := {viewBounds: {left: 76, top: 132, right: 224, bottom: 156}, text: if kDebugOn then "newton.apple.com" else "";, debug: "vRemoteText", _proto: @185 }; AddStepForm(vMain, vRemoteText); StepDeclare(vMain, vRemoteText, 'vRemoteText); _view003 := {text: "Search for:", viewBounds: {left: 8, top: 168, right: 68, bottom: 184}, viewJustify: 8388609, _proto: @218 }; AddStepForm(vMain, _view003); vRequestText := {viewBounds: {left: 76, top: 160, right: 224, bottom: 184}, text: if kDebugOn then "jim" else "";, debug: "vRequestText", _proto: @185 }; AddStepForm(vMain, vRequestText); StepDeclare(vMain, vRequestText, 'vRequestText); constant |layout_Main.t| := vMain; // End of file Main.t