// ---- End Project Data ---- // ---- File IRTimeSync.t ---- // Before Script for "vIRTimeSync" // =========================================================================================== // IRTimeSync // // Copyright © 1994 Apple Computer, Inc. // All rights reserved. // // Modification Status // YY/MM/DD Name Comments // 94/05/20 Jim Schram Initial Development during World Wide Developers Conference // 94/05/25 Jim Schram Released as |IRTimeSync:PIEDTS| version 2 sample code // 94/08/24 Jim Schram Released as version 3. Only change was to turn OFF the Copy Protected bit in Settings // ------------------------------------------------------------------------------------------------------------- // NOTE: Time synchronization is accurate +/- one second and currently does not account for beam time. // =========================================================================================== constant kDebugShowPhases := nil; // true to show phase codes in vTitle area constant kIdleFrequency := 135; // number of milliseconds between idles constant kTimeoutTicks := 900; // number of ticks to wait before timing out a connection constant kPhase_Idle := 0; // phase codes for the vTimeMachine state machine constant kPhase_CountDown := 1; constant kPhase_Send := 2; constant kPhase_Connecting := 3; constant kPhase_Receive := 5; constant kPhase_Listening := 6; constant kPhase_Abort := 7; constant kPhase_AbortQuiet := 8; constant kPhase_Disconnect := 9; constant kPhase_Disconnecting := 10; constant kPhase_Disconnected := 11; // ------------------------------------------------------------------------------------------------------------- constant kTitle := "Newton Time Synchronization Via IR Beam"; // title of the application constant kMessage_Send := "Looking for receiver..."; // progress/status messages constant kMessage_Receive := "Looking for sender..."; constant kMessage_Stopping := "Stopping..."; constant kMessage_Timeout := "No response."; constant kMessage_TimeHasBeenSet := "Time has been set."; constant kMessage_PortInUse := "IR port in use?"; constant kMessage_GenericError := "An error occured!"; DefConst('kMessage_BadTime, "Bad time value received." & unicodeCR & "Please try again."); DefConst('kMessage_CountDownFunc, // progress/status message constructor functions func(pendingSeconds) return "Time will be set in" && NumberStr(pendingSeconds) && if pendingSeconds = 1 then "second." else "seconds." & unicodeCR & "Please wait..."); DefConst('kMessage_IdleFunc, func() begin local t := TimeInSeconds() mod 60; return DateNTime(Time()) & " and " & NumberStr(t) & if t = 1 then " second" else " seconds"; end); constant kButton_Send := "Send Time"; // button names constant kButton_Receive := "Receive Time"; constant kButton_Stop := "Stop"; // ------------------------------------------------------------------------------------------------------------- DefConst('kSysBeepSyncFunc, // replaces GetRoot():SysBeep() with a similar SYNCHRONOUS version func() if userConfiguration.beepSound <> 'none and userConfiguration.soundVolume > 0 then PlaySoundSync(ROM_soundoff.(userConfiguration.beepSound))); // ------------------------------------------------------------------------------------------------------------- OpenResFile(HOME & "IRTimeSync.rsrc"); // load our attention sound (played when the time has been set) DefConst('kAlarmSound, GetSound11("IRTimeSyncAlarmSound")); // keep the size of this sample app down by using an 11Khz sample rate CloseResFile(); // =========================================================================================== vIRTimeSync := { viewSetupDoneScript: func() begin GetRoot().extrasDrawer:Close(); :MSetPhase(nil); :SetUpIdle(135); end, viewQuitScript: func() begin if fEndPoint then fEndPoint:MDisconnect(); end, viewFlags: 576, viewIdleScript: func() begin if kDebugShowPhases then begin local xT := NumberStr(TimeInSeconds() mod 60); local xPh := if fPhase then NumberStr(fPhase) else "NIL"; local xCon := if fEndPoint and fEndPoint.fIsConnected then "TRUE" else "FALSE"; local xSt := if fEndPoint then NumberStr(fEndPoint:State()) else "NIL"; SetValue(vTitle, 'text, "T: " & xT & " Ph: " & xPh & " Con: " & xCon & " St: " & xSt ); RefreshViews(); end; if not fPhase or fPhase = kPhase_Idle then begin :MSetMessage(call kMessage_IdleFunc with ()); end else if fPhase = kPhase_CountDown then begin local elapsedTime := Abs(timeinseconds() - fStartTime); local secondsToWait := 60 - fSeconds; if elapsedTime >= secondsToWait then begin SetTime(fMinutes + 1); :MSetMessage(kMessage_TimeHasBeenSet); PlaySound(kAlarmSound); fMinutes := nil; fSeconds := nil; :MSetPhase(kPhase_Disconnect); end else :MSetMessage(call kMessage_CountDownFunc with (secondsToWait - elapsedTime)); end else if fPhase = kPhase_Send then begin fEndPoint := { _proto: fEndPointConfig, _parent: self }; local err := fEndPoint:Instantiate(fEndPoint, nil); if err then begin :MSetError(kMessage_PortInUse, err); :MSetPhase(nil); end else begin vReceiveTime:Hide(); SetValue(vSendTime, 'text, kButton_Stop); :MSetMessage(kMessage_Send); fEndPoint:MConnect(); fTimeoutTicks := Ticks(); :MSetPhase(kPhase_Connecting); end; end else if fPhase = kPhase_Connecting then begin if Abs(Ticks() - fTimeoutTicks) > kTimeoutTicks then begin :MSetMessage(kMessage_Timeout); call kSysBeepSyncFunc with (); call kSysBeepSyncFunc with (); :MSetPhase(kPhase_AbortQuiet); end else if fEndPoint.fIsConnected then begin fEndPoint:MSendTime(); :MSetPhase(kPhase_Disconnect); end; end else if fPhase = kPhase_Disconnect then begin if fEndPoint then begin fEndPoint:MDisconnect(); :MSetPhase(kPhase_Disconnecting); end else :MSetPhase(kPhase_Disconnected); end else if fPhase = kPhase_Disconnecting then begin end else if fPhase = kPhase_Disconnected then begin SetValue(vReceiveTime, 'text, kButton_Receive); SetValue(vSendTime, 'text, kButton_Send); vReceiveTime:Show(); vSendTime:Show(); :MSetPhase(nil); end else if fPhase = kPhase_Abort then begin :MSetMessage(kMessage_Stopping); :MSetPhase(kPhase_AbortQuiet); end else if fPhase = kPhase_AbortQuiet then begin :MSetPhase(kPhase_Disconnect); end else if fPhase = kPhase_Receive then begin fEndPoint := { _proto: fEndPointConfig, _parent: self }; local err := fEndPoint:Instantiate(fEndPoint, nil); if err then begin :MSetError(kMessage_PortInUse, err); :MSetPhase(nil); end else begin vSendTime:Hide(); SetValue(vReceiveTime, 'text, kButton_Stop); :MSetMessage(kMessage_Receive); fEndPoint:MListen(); fTimeoutTicks := Ticks(); :MSetPhase(kPhase_Listening); end; end else if fPhase = kPhase_Listening then begin if Abs(Ticks() - fTimeoutTicks) > kTimeoutTicks then begin :MSetMessage(kMessage_Timeout); call kSysBeepSyncFunc with (); call kSysBeepSyncFunc with (); :MSetPhase(kPhase_AbortQuiet); end else if fEndPoint.fIsConnected and fMinutes and fSeconds then begin if fMinutes > 0 and fSeconds >=0 and fSeconds <= 59 then :MSetPhase(kPhase_CountDown) else begin :MSetMessage(kMessage_BadTime); call kSysBeepSyncFunc with (); call kSysBeepSyncFunc with (); :MSetPhase(kPhase_AbortQuiet); end; end; end; return kIdleFrequency; end, fPhase: nil // holds the next phase to be executed for the idle machine, fEndPointConfig: { _proto: protoEndpoint, fIsConnected: nil, receiveFlags: kFrame, configOptions: [ { label: kCMSSlowIR, type: 'service, opCode: opSetRequired, }, ], exceptionHandler: func(exception) // deferred actions exception context is LAME -- the only action we dare take is to say we've handled the exception begin if HasSlot(exception, 'data) and exception.data <> nil and exception.data <> -16005 // cancelled (not really an error) and exception.data <> -16013 then begin // cancelled (not really an error) :MSetError(kMessage_GenericError, exception.data); call kSysBeepSyncFunc with (); call kSysBeepSyncFunc with (); call kSysBeepSyncFunc with (); if fPhase then if fPhase = kPhase_Connecting or fPhase = kPhase_Listening or fPhase = kPhase_CountDown then :MSetPhase(kPhase_Abort); end; return true; end, FReceiveTime: { inputForm: 'string, // data is a string of this format: IRTimeSyncVersion1X123456Y123456Z endCharacter: $Z, inputScript: func(ep, data) begin ep._parent.fStartTime := TimeInSeconds(); // note the time we received the beam ep:SetInputSpec(nil); // don't want to be retriggered ep._parent.fMinutes := 0; ep._parent.fSeconds := 0; local p1 := StrPos(data, "X", 0); // suck out the version info if p1 then begin if StrEqual(SubStr(data, 0, p1), "IRTimeSyncVersion1") then begin local p2 := StrPos(data, "Y", p1); // suck out the time in minutes (since midnight January 1, 1904) if p2 then begin local p3 := StrPos(data, "Z", p2); // suck out the time in seconds (0-59) if p3 then begin ep._parent.fMinutes := RintToL(StringToNumber(SubStr(data, p1 + 1, p2 - p1 - 1))); ep._parent.fSeconds := RintToL(StringToNumber(SubStr(data, p2 + 1, p3 - p2 - 1))); end; end; end else ep._parent:MSetError(kMessage_BadTime, -666); end; end, }, MSendTime: func() begin local s := "IRTimeSyncVersion1X" // IR Time Sync Version ID & NumberStr(Time()) & "Y" // minutes since midnight January 1, 1904 & NumberStr(TimeInSeconds() mod 60) & "Z"; // seconds past the minute (0-59) :Output(s, kFrame); :FlushOutput(); end, MListen: func() begin fMinutes := nil; fSeconds := nil; AddDeferredAction( func(ep) begin ep.fIsConnected := ep:Listen(nil) = nil; if ep.fIsConnected then ep:SetInputSpec(ep.FReceiveTime); end, [self]); end, MConnect: func() begin AddDeferredAction( func(ep) begin ep.fIsConnected := ep:Connect(nil, nil) = nil; end, [self]); end, MDisconnect: func() begin self.nextInputSpec := nil; :SetInputSpec(nil); if fIsConnected and :State() = kDataXfer then :Release() else :Abort(); AddDelayedAction(func(ep) begin if ep.fIsConnected then ep:Disconnect(); ep:Dispose(); ep._parent.fEndPoint := nil; if ep._parent.fPhase then ep._parent:MSetPhase(kPhase_Disconnected); end, [self], 666); end, }, viewBounds: {left: 0, top: 0, right: 228, bottom: 110}, _proto: protoFloater, MSetPhase: func(phase) begin fPhase := phase; end, fStartTime: 0, viewJustify: 80, fMinutes: nil // nil until we receive ANY value, 0 = illegal value, other = number of minutes since midnight January 1, 1904 , fEndPoint: nil, declareSelf: 'base, viewClickScript: func(unit) begin :Drag(unit, nil); end, debug: "vIRTimeSync", fTimeoutTicks: 0, MSetError: func(message, error) begin :MSetMessage(message & unicodeCR & NumberStr(error)); call kSysBeepSyncFunc with (); end, fSeconds: nil // nil until we receive ANY value, 0 to 59 (inclusive) are legal values, other = illegal values , MSetMessage: func(message) begin SetValue(vMessage, 'text, Clone(message)); RefreshViews(); end }; vTitle := /* child of vIRTimeSync */ {text: "", viewBounds: {left: 0, top: 4, right: 0, bottom: 20}, viewJustify: 8388658, _proto: protoStaticText, debug: "vTitle" }; // View vTitle is declared to vIRTimeSync // After Script for "vTitle" thisView := vTitle; thisView.text := kTitle; vMessage := /* child of vIRTimeSync */ {text: "", viewBounds: {left: 16, top: 24, right: 208, bottom: 55}, viewJustify: 2, viewFont: simpleFont9, _proto: protoStaticText, debug: "vMessage" }; // View vMessage is declared to vIRTimeSync vReceiveTime := /* child of vIRTimeSync */ {text: "", buttonClickScript: func() begin if not fPhase then :MSetPhase(kPhase_Receive) else if fPhase = kPhase_Listening or fPhase = kPhase_CountDown then :MSetPhase(kPhase_Abort); end, viewBounds: {left: 18, top: 57, right: 102, bottom: 77}, _proto: protoTextButton, debug: "vReceiveTime" }; // View vReceiveTime is declared to vIRTimeSync // After Script for "vReceiveTime" thisView := vReceiveTime; thisView.text := kButton_Receive; vSendTime := /* child of vIRTimeSync */ {text: "", buttonClickScript: func() begin if not fPhase then :MSetPhase(kPhase_Send) else if fPhase = kPhase_Connecting then :MSetPhase(kPhase_Abort); end, viewBounds: {left: 122, top: 57, right: 206, bottom: 77}, _proto: protoTextButton, debug: "vSendTime" }; // View vSendTime is declared to vIRTimeSync // After Script for "vSendTime" thisView := vSendTime; thisView.text := kButton_Send; vStatusBar := /* child of vIRTimeSync */ {_proto: protoStatus, debug: "vStatusBar"}; // ---- Beginning of section for non used Layout files ---- // End of output