From 380c45917f2dd6c98786bd0d33748afacf41b4ac Mon Sep 17 00:00:00 2001 From: John Wolfe Date: Wed, 9 Jul 2014 17:40:44 -0700 Subject: [PATCH] #27 - Added most of the remaining support for workout goals. Remaining work includes: - notify UI of current goal achievement status - add support to animated watch for indicating goal achievement status - ensure in-flight goal-achievement timer events are handled properly after cancellation of timer - make GoalSpec attribute names follow convention (lower case first letter) - test workout goal handling with Verifier --- .../GPS Watch/Library/Tracking/Tracking.xtuml | 11 +- .../Tracking/Achievement/Achievement.xtuml | 27 +- .../Library/Tracking/Tracking/Goal/Goal.xtuml | 192 ++++++++++++- .../InstanceStateMachine.xtuml | 61 ++-- .../Goal/evaluateCompletion__Operation.oal | Bin 0 -> 472 bytes .../Library/Tracking/Tracking/Tracking.xtuml | 270 ++++++++++++++---- .../currentSpeed__Attribute.oal | Bin 525 -> 0 bytes .../TrackingDataTypes/TrackingDataTypes.xtuml | 53 +++- 8 files changed, 506 insertions(+), 108 deletions(-) create mode 100644 applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/evaluateCompletion__Operation.oal delete mode 100644 applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/WorkoutSession/currentSpeed__Attribute.oal diff --git a/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking.xtuml b/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking.xtuml index aa80f21a7..b0d8947be 100644 --- a/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking.xtuml +++ b/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking.xtuml @@ -713,7 +713,16 @@ INSERT INTO SPR_RS VALUES ("d9d00089-99ea-429a-9af0-86fc7db383e8", 'setTargetPressed', '', - '', + '// Find the singleton instance of workout session, and if there is a +// goal currently executing for it, notify it that its execution +// has completed. +select any session from instances of WorkoutSession; +if ( not empty session ) + select one goal related by session->Goal[R11.''is currently executing'']; + if ( not empty goal ) + generate Goal1:Completed to goal; + end if; +end if;', 1); INSERT INTO C_EP_PROXY VALUES ("3e2d7574-ba3f-4a52-90c2-6787610b555a", diff --git a/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Achievement/Achievement.xtuml b/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Achievement/Achievement.xtuml index e5ce1ece3..84dc5bd35 100644 --- a/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Achievement/Achievement.xtuml +++ b/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Achievement/Achievement.xtuml @@ -5,9 +5,30 @@ INSERT INTO O_OBJ 'Achievement', 10, 'Achievement', - 'Each instance represents one contiguous period of time during + 'Each instance represents one contiguous period of time during which a particular goal was being met (achieved).', "00000000-0000-0000-0000-000000000000"); +INSERT INTO O_TFR + VALUES ("6fac41c9-c339-4dbe-8651-11ff5f9db899", + "313e48f7-0516-49bd-9401-5c67c2e55db9", + 'close', + '', + "ba5eda7a-def5-0000-0000-000000000000", + 1, + '// Close this open achievement record by storing the end time, relating this +// record as a recorded one and unrelating it as the open one. +select one goal related by self->Goal[R14.''is open for'']; +select one workoutTimer related by + goal->WorkoutSession[R11.''is currently executing within'']->WorkoutTimer[R8.''is timed by'']; + self.endTime = workoutTimer.time; +unrelate self from goal across R14.''is open for''; +relate self to goal across R12.''specifies achievement of''; + + +', + 1, + '', + "00000000-0000-0000-0000-000000000000"); INSERT INTO O_NBATTR VALUES ("b3949e0b-65ec-4e72-82be-b899f2e3b63b", "313e48f7-0516-49bd-9401-5c67c2e55db9"); @@ -19,7 +40,7 @@ INSERT INTO O_ATTR "313e48f7-0516-49bd-9401-5c67c2e55db9", "00000000-0000-0000-0000-000000000000", 'startTime', - 'Starting time for this achievement, expressed as the number of seconds + 'Starting time for this achievement, expressed as the number of seconds since the beginning of the associated workout session.', '', 'startTime', @@ -38,7 +59,7 @@ INSERT INTO O_ATTR "313e48f7-0516-49bd-9401-5c67c2e55db9", "b3949e0b-65ec-4e72-82be-b899f2e3b63b", 'endTime', - 'Ending time for this achievement, expressed as the number of seconds + 'Ending time for this achievement, expressed as the number of seconds since the beginning of the associated workout session.', '', 'endTime', diff --git a/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/Goal.xtuml b/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/Goal.xtuml index c4e71f7a0..3145ae3c2 100644 --- a/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/Goal.xtuml +++ b/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/Goal.xtuml @@ -16,8 +16,31 @@ INSERT INTO O_TFR '', "ba5eda7a-def5-0000-0000-000000000000", 0, - '', - 3, + '// Create and activate an instance of a goal specified by +// the goal specification identified by the incoming parameter. + +// Find the goal specification for this goal, then +// create and relate this goal to the specification +// and to the singleton workout session. +select any goalSpec from instances of GoalSpec + where ( selected.SequenceNumber == param.sequenceNumber ); +create object instance goal of Goal; +relate goal to goalSpec across R9.''specified by''; +select any session from instances of WorkoutSession; +relate goal to session across R11.''is currently executing within''; + +// Initialize this goal. +goal.calculateStart(); +goal.isBeingAchieved = false; + +// Start a timer that periodically causes evaluation of goal achievement. +create event instance evaluateEvent of Goal2:Evaluate to goal; +goal.evaluationTimer = TIM::timer_start_recurring( event_inst: evaluateEvent, microseconds: evaluationPeriod ); + + + +', + 1, '', "00000000-0000-0000-0000-000000000000"); INSERT INTO O_TPARM @@ -29,6 +52,129 @@ INSERT INTO O_TPARM '', "00000000-0000-0000-0000-000000000000", 'Sequence number of the goal specification for which an instance of a goal should be created.'); +INSERT INTO O_TFR + VALUES ("b3215204-3d02-48ba-9794-25ddbc40009c", + "c1022670-c44d-401e-bb23-dfcef6084fba", + 'calculateStart', + '', + "ba5eda7a-def5-0000-0000-000000000000", + 1, + '// Based on the span type for this goal, calculate and +// store the value of the start attribute. +select one goalSpec related by self->GoalSpec[R9.''specified by'']; +if ( goalSpec.SpanType == GoalSpan::Time ) + select one workoutTimer related by + self->WorkoutSession[R11.''is currently executing within'']->WorkoutTimer[R8.''is timed by'']; + self.start = workoutTimer.time; +elif ( goalSpec.SpanType == GoalSpan::Distance ) + select one session related by self->WorkoutSession[R11.''is currently executing within'']; + self.start = session.accumulatedDistance; +else + LOG::LogFailure( message: "Goal.calculateStart: Unknown Goal Span Type." ); +end if;', + 1, + '', + "8ac30761-d43a-49d9-b7e4-c3ec442ea9f3"); +INSERT INTO O_TFR + VALUES ("2b884ff8-637a-468b-a2fa-f96b8ef98b0a", + "c1022670-c44d-401e-bb23-dfcef6084fba", + 'evaluateAchievement', + '', + "ba5eda7a-def5-0000-0000-000000000000", + 1, + '// Based on the type of goal, determine whether this one is +// currently being achieved and update the appropriate attribute. + +// Find the related goal specification and workout session for this goal. +select one goalSpec related by self->GoalSpec[R9.''specified by'']; +select one session related by self->WorkoutSession[R11.''is currently executing within'']; + +// Based on the goal criteria type, get the appropriate current value +// for comparison against the criteria (minimum and maximum). +currentValue = 0.0; // Explicit declaration +if ( goalSpec.CriteriaType == GoalCriteria::HeartRate ) + currentValue = session.currentHeartRate; +elif ( goalSpec.CriteriaType == GoalCriteria::Pace ) + currentValue = session.currentPace; +else + LOG::LogFailure( message: "Goal.evaluateAchievement: Unknown Goal Criteria Type." ); +end if; + +// Save current value for handling of achievement records. +previousAchievement = self.isBeingAchieved; + +// Compare the current value against the criteria and update the attribute +// indicating goal achievement status. +if ( (goalSpec.Minimum <= currentValue) and (currentValue <= goalSpec.Maximum) ) + self.isBeingAchieved = true; +else + self.isBeingAchieved = false; +end if; + +// Update achievement records if necessary. There are four cases: +// 0. Still not achieving this goal. +// 1. Just started achieving this goal. +// 2. Just stopped achieving this goal. +// 3. Still achieving this goal. +// For cases 0 and 3, there is nothing to be done for achievement records. +// Case 1 requires the creation of a new achievment record, storing the start time. +// Case 2 requires the storing of the end time for the open achievement record. +if ( (not previousAchievement) and self.isBeingAchieved ) + // Case 1, create achievement record, store start time, relate it as open record. + create object instance achievement of Achievement; + relate self to achievement across R14.''has open''; + select one workoutTimer related by session->WorkoutTimer[R8.''is timed by'']; + achievement.startTime = workoutTimer.time; +elif ( previousAchievement and (not self.isBeingAchieved) ) + // Case 2, store end time, relate as recorded record, unrelate as open record. + select one achievement related by self->Achievement[R14.''has open'']; + achievement.close(); +end if; +', + 1, + '', + "b3215204-3d02-48ba-9794-25ddbc40009c"); +INSERT INTO O_TFR + VALUES ("7090ad22-2865-4836-a89f-a46b868d3ac0", + "c1022670-c44d-401e-bb23-dfcef6084fba", + 'evaluateCompletion', + '', + "ba5eda7a-def5-0000-0000-000000000000", + 1, + '// Based on the span type for this goal, determine +// whether it has been completed, update achievement +// records as necessary, and advance to the next goal +// if appropriate. + +select one goalSpec related by self->GoalSpec[R9.''specified by'']; + +// Based on the span type for the goal, get the elapsed span +// for comparison against the specified span for the goal. +elapsedSpan = 0.0; // Explicit declaration +if ( goalSpec.SpanType == GoalSpan::Distance ) + select one session related by self->WorkoutSession[R11.''is currently executing within'']; + elapsedSpan = session.accumulatedDistance - self.start; +elif ( goalSpec.SpanType == GoalSpan::Time ) + select one workoutTimer related by + self->WorkoutSession[R11.''is currently executing within'']->WorkoutTimer[R8.''is timed by'']; + elapsedSpan = workoutTimer.time - self.start; +else + LOG::LogFailure( message: "Goal.evaluateCompletion: Unknown Goal Span Type." ); +end if; + +// Compare the current value against the specified span to determine +// whether the execution of this workout goal is complete. +if ( elapsedSpan >= goalSpec.Span ) + select one openAchievement related by self->Achievement[R14.''has open'']; + if ( not empty openAchievement ) + openAchievement.close(); + end if; + generate Goal1:Completed to self; +end if; +', + 1, + '', + "2b884ff8-637a-468b-a2fa-f96b8ef98b0a"); INSERT INTO O_NBATTR VALUES ("fa9000ac-0495-47fe-bbaf-66bc8f9a4f0a", "c1022670-c44d-401e-bb23-dfcef6084fba"); @@ -58,17 +204,17 @@ INSERT INTO O_ATTR "c1022670-c44d-401e-bb23-dfcef6084fba", "645a58ca-7e54-4f1b-bb56-64249dab5aaa", 'start', - 'Captures the starting point of the span for this particular goal so -that the end of the goal execution period can be determined. In other -words, using the value of this attribute together with the span specified -by the associated goal specification, the goal knows it is finished. - -For distance-based goals, it is expressed as the accumulated distance -in meters for the associated workout session at the time this goal -execution commenced. - -For time-based goals, it is expressed as the elapsed time in seconds -for the associated workout session at the time this goal execution + 'Captures the starting point of the span for this particular goal so +that the end of the goal execution period can be determined. In other +words, using the value of this attribute together with the span specified +by the associated goal specification, the goal knows when it is finished. + +For distance-based goals, it is expressed as the accumulated distance +in meters for the associated workout session at the time this goal +execution commenced. + +For time-based goals, it is expressed as the elapsed time in seconds +for the associated workout session at the time this goal execution commenced.', '', 'start', @@ -103,7 +249,7 @@ INSERT INTO O_BATTR INSERT INTO O_ATTR VALUES ("c307e400-f06b-4450-8e3c-94dadad961ff", "c1022670-c44d-401e-bb23-dfcef6084fba", - "fa9000ac-0495-47fe-bbaf-66bc8f9a4f0a", + "0078004b-9cab-4a56-beac-5014d746ad80", 'current_state', '', '', @@ -112,6 +258,24 @@ INSERT INTO O_ATTR "ba5eda7a-def5-0000-0000-000000000006", '', ''); +INSERT INTO O_NBATTR + VALUES ("0078004b-9cab-4a56-beac-5014d746ad80", + "c1022670-c44d-401e-bb23-dfcef6084fba"); +INSERT INTO O_BATTR + VALUES ("0078004b-9cab-4a56-beac-5014d746ad80", + "c1022670-c44d-401e-bb23-dfcef6084fba"); +INSERT INTO O_ATTR + VALUES ("0078004b-9cab-4a56-beac-5014d746ad80", + "c1022670-c44d-401e-bb23-dfcef6084fba", + "fa9000ac-0495-47fe-bbaf-66bc8f9a4f0a", + 'evaluationTimer', + 'Handle for the timer used for periodic evaluation of goal achievement.', + '', + 'evaluationTimer', + 0, + "ba5eda7a-def5-0000-0000-00000000000f", + '', + ''); INSERT INTO O_ID VALUES (0, "c1022670-c44d-401e-bb23-dfcef6084fba"); diff --git a/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/InstanceStateMachine/InstanceStateMachine.xtuml b/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/InstanceStateMachine/InstanceStateMachine.xtuml index 201803d9e..8712eb054 100644 --- a/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/InstanceStateMachine/InstanceStateMachine.xtuml +++ b/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/InstanceStateMachine/InstanceStateMachine.xtuml @@ -22,11 +22,11 @@ INSERT INTO SM_EVT "7fc80988-4415-437c-acc5-4c130cc2715d", "00000000-0000-0000-0000-000000000000", 1, - 'SpanExhausted', + 'Completed', 0, '', 'Goal1', - 'Indicates that the span for this particular goal has been exhausted.'); + 'Indicates that execution of this goal has been completed. '); INSERT INTO SM_LEVT VALUES ("5706c37b-a57c-4258-ad16-eba5b972f5bf", "7fc80988-4415-437c-acc5-4c130cc2715d", @@ -123,8 +123,13 @@ INSERT INTO SM_AH INSERT INTO SM_ACT VALUES ("68a5f818-7dab-42d9-8593-79e79e2cc28b", "7fc80988-4415-437c-acc5-4c130cc2715d", - 3, - '', + 1, + '// Determine whether this goal is currently being achieved. +self.evaluateAchievement(); + +// Determine whether execution of this goal is complete, +// and if so, advance to the next one if it exists. +self.evaluateCompletion();', ''); INSERT INTO SM_MOAH VALUES ("4c799586-a213-493c-8525-7d7046820ad2", @@ -139,14 +144,18 @@ INSERT INTO SM_ACT 1, '// Stop executing this goal and begin executing the next one, if there is one. -// First, add this goal to the collection of those that have already executed. +// Stop the goal-evaluation timer, ignoring the return code which indicates +// whether a timer event was in flight when the timer was cancelled. +cancelSucceeded = TIM::timer_cancel( timer_inst_ref: self.evaluationTimer ); + +// Add this goal to the collection of those that have already executed. select one session related by self->WorkoutSession[R11.''is currently executing within'']; relate self to session across R13.''was executed within''; -// Next, remove this goal from the collection of currently executing ones. +// Remove this goal from the collection of currently executing ones. unrelate self from session across R11.''is currently executing within''; -// Finally, find the next goal specification in the sequence and start executing it, if it exists. +// Find the next goal specification in the sequence and start executing it, if it exists. select one currentGoalSpec related by self->GoalSpec[R9.''specified by'']; select any nextGoalSpec related by session->GoalSpec[R10.''includes''] @@ -214,11 +223,11 @@ INSERT INTO GD_SHP INSERT INTO GD_NCS VALUES ("11ca3dfe-8c51-49db-951f-4bcf9c349dd7"); INSERT INTO DIM_ND - VALUES (204.000000, + VALUES (349.000000, 144.000000, "11ca3dfe-8c51-49db-951f-4bcf9c349dd7"); INSERT INTO DIM_GE - VALUES (4200.000000, + VALUES (4104.000000, 3084.000000, "11ca3dfe-8c51-49db-951f-4bcf9c349dd7", "00000000-0000-0000-0000-000000000000"); @@ -228,7 +237,7 @@ INSERT INTO DIM_ELE "00000000-0000-0000-0000-000000000000"); INSERT INTO DIM_CON VALUES ("bf1c05cb-62f4-4158-a22c-f1eea42f07d2", - 4404.000000, + 4453.000000, 3132.000000, "11ca3dfe-8c51-49db-951f-4bcf9c349dd7"); INSERT INTO DIM_CON @@ -238,7 +247,7 @@ INSERT INTO DIM_CON "11ca3dfe-8c51-49db-951f-4bcf9c349dd7"); INSERT INTO DIM_CON VALUES ("0bbfb44f-3419-4927-b876-00cde45ded43", - 4200.000000, + 4104.000000, 3168.000000, "11ca3dfe-8c51-49db-951f-4bcf9c349dd7"); INSERT INTO DIM_CON @@ -258,11 +267,11 @@ INSERT INTO GD_SHP INSERT INTO GD_NCS VALUES ("70725233-fd31-4b3f-8009-40b0e332d040"); INSERT INTO DIM_ND - VALUES (204.000000, - 156.000000, + VALUES (481.000000, + 409.000000, "70725233-fd31-4b3f-8009-40b0e332d040"); INSERT INTO DIM_GE - VALUES (4656.000000, + VALUES (4704.000000, 3084.000000, "70725233-fd31-4b3f-8009-40b0e332d040", "00000000-0000-0000-0000-000000000000"); @@ -272,7 +281,7 @@ INSERT INTO DIM_ELE "00000000-0000-0000-0000-000000000000"); INSERT INTO DIM_CON VALUES ("6671936b-d4ef-4c36-95c4-d01cd8e0969b", - 4656.000000, + 4704.000000, 3132.000000, "70725233-fd31-4b3f-8009-40b0e332d040"); INSERT INTO DIM_CON @@ -286,7 +295,7 @@ INSERT INTO GD_GE "4d3b865b-a2b5-49a9-8f19-a163f108922a", 42, 0, - 'GPS Watch::Library::Tracking::Tracking::Goal::Instance State Machine::Executing::Goal1: SpanExhausted'); + 'GPS Watch::Library::Tracking::Tracking::Goal::Instance State Machine::Executing::Goal1: Completed'); INSERT INTO GD_CON VALUES ("a8cd31e1-a403-49b2-82c8-0177f95634c1", "00000000-0000-0000-0000-000000000000"); @@ -320,7 +329,7 @@ INSERT INTO DIM_ND 0.000000, "70c82ccc-754d-4204-969b-f9f10d735b95"); INSERT INTO DIM_GE - VALUES (4413.000000, + VALUES (4461.500000, 3103.000000, "70c82ccc-754d-4204-969b-f9f10d735b95", "00000000-0000-0000-0000-000000000000"); @@ -355,14 +364,14 @@ INSERT INTO GD_LS "c7c86751-1ec7-4a14-bfc5-6efe3cf22dc0"); INSERT INTO DIM_WAY VALUES ("fc9c1379-7b56-49eb-ae7d-9e56700c41de", - 4404.000000, + 4453.000000, 3132.000000, "a8cd31e1-a403-49b2-82c8-0177f95634c1", "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000"); INSERT INTO DIM_WAY VALUES ("c7c86751-1ec7-4a14-bfc5-6efe3cf22dc0", - 4656.000000, + 4704.000000, 3132.000000, "a8cd31e1-a403-49b2-82c8-0177f95634c1", "00000000-0000-0000-0000-000000000000", @@ -420,8 +429,8 @@ INSERT INTO DIM_ND 0.000000, "e1d94218-45bd-4a86-b11b-6f59bcb293cf"); INSERT INTO DIM_GE - VALUES (4081.000000, - 3136.000000, + VALUES (4063.000000, + 3288.000000, "e1d94218-45bd-4a86-b11b-6f59bcb293cf", "00000000-0000-0000-0000-000000000000"); INSERT INTO DIM_ELE @@ -473,7 +482,7 @@ INSERT INTO GD_LS "2eaff31b-b4cc-40a6-85bb-c6fe24d1a178"); INSERT INTO DIM_WAY VALUES ("50902b0d-47ea-4166-939d-cde182942486", - 4200.000000, + 4104.000000, 3168.000000, "0907b6e7-e62c-41be-99cd-693a2cd24c46", "00000000-0000-0000-0000-000000000000", @@ -487,22 +496,22 @@ INSERT INTO DIM_WAY "50902b0d-47ea-4166-939d-cde182942486"); INSERT INTO DIM_WAY VALUES ("58a283a5-d132-4f2e-8367-6207efd9ca28", - 4150.000000, + 4054.000000, 3168.000000, "0907b6e7-e62c-41be-99cd-693a2cd24c46", "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000"); INSERT INTO DIM_WAY VALUES ("83eee3fc-a8fa-4232-a079-2b24acf13826", - 4150.000000, - 3277.000000, + 4054.000000, + 3276.000000, "0907b6e7-e62c-41be-99cd-693a2cd24c46", "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000"); INSERT INTO DIM_WAY VALUES ("d8153263-61d3-4146-9854-5211354cb02b", 4320.000000, - 3277.000000, + 3276.000000, "0907b6e7-e62c-41be-99cd-693a2cd24c46", "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000"); diff --git a/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/evaluateCompletion__Operation.oal b/applications/gps/GPS Watch/models/GPS Watch/Library/Tracking/Tracking/Goal/evaluateCompletion__Operation.oal new file mode 100644 index 0000000000000000000000000000000000000000..7d9bca2a19f02589016de162d5593b2bd2aba6d2 GIT binary patch literal 472 zcmZ{h!A`Dhg9!)qHPj-udSoCC+5R$4yOgx&QU14?Gt=lOU4< z0($rEd+-i!g&+sLOeXXHyYv0KuOleL8n!(Zuz-i8Gz$|?@+4;6h$Vs%Pl=rem)lYh9=2_!aa@DZvIGpiGeXk$EG@NvLqYcouPuSxc}p Y!~AcENtiJ(!PehDSp)Pvw>c<>Lz zAJDsZ|D*rl#+Is}mt}WmcHhkG^AKiI1)E$(ED|wE#o~;IB1@TTzSJOoL=Z=Lx}?dC=}ux=8%e{L-dB05|Xy zs)2c`koUEN?@d>#zRaj2(ln5<`TNLn%A!!Wv0Ox8g-JFsnLm82J-*}9G)x^rISuYa zp6DV