اشکال زدایی خانه محلی

1. قبل از شروع

ادغام خانه های هوشمند به دستیار Google اجازه می دهد تا دستگاه های متصل در خانه کاربران را کنترل کند. برای ایجاد یک ادغام Cloud-to-Cloud، باید یک نقطه پایانی وب هوک ابری ارائه دهید که بتواند اهداف خانه هوشمند را مدیریت کند. به عنوان مثال، وقتی کاربری می گوید: «Hey Google، چراغ ها را روشن کن»، دستیار دستوری را برای به روز رسانی وضعیت دستگاه به اجرای ابری شما ارسال می کند.

Local Home SDK با افزودن یک مسیر محلی برای مسیریابی اهداف خانه هوشمند به طور مستقیم به یک دستگاه Google Home، ادغام خانه هوشمند شما را بهبود می بخشد، که قابلیت اطمینان را افزایش می دهد و تأخیر در پردازش دستورات کاربران را کاهش می دهد. این به شما امکان می دهد یک برنامه تکمیل محلی را در TypeScript یا JavaScript بنویسید و اجرا کنید که دستگاه ها را شناسایی می کند و دستورات را در هر بلندگوی هوشمند Google Home یا نمایشگر هوشمند Google Nest اجرا می کند. سپس برنامه شما مستقیماً با دستگاه‌های هوشمند موجود کاربران از طریق شبکه محلی با استفاده از پروتکل‌های استاندارد موجود برای اجرای دستورات ارتباط برقرار می‌کند.

72ffb320986092c.png

اشکال زدایی ادغام های Cloud-to-Cloud یک گام مهم برای ایجاد ادغام های شما با کیفیت تولید است، با این حال بدون ابزارهای آموزنده و آسان برای عیب یابی و آزمایش، چالش برانگیز و وقت گیر است. برای تسهیل اشکال‌زدایی ادغام‌های Cloud-to-Cloud، معیارها و Logging و Test Suite Google Cloud Platform (GCP) برای خانه‌های هوشمند برای کمک به شناسایی و حل مشکلات ادغام‌های خود در دسترس هستند.

پیش نیازها

چیزی که خواهی ساخت

در این نرم‌افزار کد، یک تکمیل محلی برای ادغام‌های Cloud-to-Cloud ایجاد می‌کنید و آن را به دستیار متصل می‌کنید، سپس برنامه Local Home را از طریق مجموعه آزمایشی برای خانه‌های هوشمند و معیارها و گزارش‌گیری Google Cloud Platform (GCP) اشکال‌زدایی می‌کنید.

چیزی که یاد خواهید گرفت

  • نحوه استفاده از معیارهای GCP و Logging برای شناسایی و حل مشکلات تولید.
  • نحوه استفاده از Test Suite برای شناسایی مشکلات عملکردی و API.
  • نحوه استفاده از Chrome Dev Tools هنگام توسعه برنامه Local Home.

آنچه شما نیاز دارید

2. برنامه شستشو را اجرا کنید

کد منبع را دریافت کنید

برای دانلود نمونه این کد لبه روی دستگاه توسعه خود، روی لینک زیر کلیک کنید:

یا می توانید مخزن GitHub را از خط فرمان کلون کنید:

$ git clone https://github.com/google-home/smarthome-debug-local.git

در مورد پروژه

برنامه شروع کننده شامل زیردایرکتوری ها و عملکردهای ابری مشابه به عنوان Enable local realization for Cloud-to-cloud integrations codelab است. اما به جای app-start ، ما در اینجا app-faulty داریم. ما با یک برنامه خانگی محلی شروع خواهیم کرد که کار می کند اما نه به خوبی.

به Firebase متصل شوید

ما از همان پروژه‌ای استفاده می‌کنیم که شما در «فعال کردن تحقق محلی برای کد لبه ادغام‌های ابر به ابر» ایجاد کرده‌اید، اما فایل‌های دانلود شده را در این لبه کد اجرا می‌کنیم.

به دایرکتوری app-faulty بروید، سپس Firebase CLI را با پروژه ادغام خود که در لبه کد Enable local realization for Cloud-to-cloud integrations ایجاد شده است، راه اندازی کنید:

$ cd app-faulty
$ firebase use <project-id>

در Firebase مستقر شوید

به پوشه app-faulty/functions بروید و تمام وابستگی های لازم را با استفاده از npm نصب کنید:

$ cd functions
$ npm install

توجه: اگر پیام زیر را مشاهده کردید، می توانید نادیده بگیرید و ادامه دهید. این هشدار به دلیل برخی وابستگی‌های قدیمی است و می‌توانید جزئیات بیشتری را در اینجا بیابید.

found 5 high severity vulnerabilities
  run `npm audit fix` to fix them, or `npm audit` for details

به دایرکتوری app-faulty/local/ بروید و دستورات زیر را برای دانلود کامپایلر TypeScript و کامپایل برنامه اجرا کنید:

$ cd ../local
$ npm install
$ npm run build

این منبع index.ts (TypeScript) را کامپایل می کند و محتویات زیر را در دایرکتوری app-faulty/public/local-home/ قرار می دهد:

  • bundle.js - خروجی جاوا اسکریپت کامپایل شده حاوی برنامه محلی و وابستگی ها.
  • index.html — صفحه میزبانی محلی که برای ارائه برنامه برای آزمایش روی دستگاه استفاده می شود.

اکنون که وابستگی ها را نصب کرده اید و پروژه خود را پیکربندی کرده اید، برای اولین بار آماده اجرای برنامه هستید.

$ firebase deploy

این خروجی کنسولی است که باید ببینید:

...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/<project-id>/overview
Hosting URL: https://<projectcd -id>.web.app

این دستور یک برنامه وب را به همراه چندین تابع Cloud برای Firebase مستقر می کند.

HomeGraph را به روز کنید

برای مشاهده برنامه وب، URL میزبانی را در مرورگر خود باز کنید ( https://<project-id>.web.app ). در رابط کاربری وب، روی Refresh کلیک کنید ae8d3b25777a5e30.png دکمه برای به‌روزرسانی HomeGraph از طریق درخواست همگام‌سازی با آخرین ابرداده دستگاه از برنامه شستشوی معیوب:

fa3c47f293cfe0b7.png

برنامه Google Home را باز کنید و بررسی کنید که می‌توانید دستگاه لباسشویی خود را با نام جدید «واشر معیوب» ببینید. به یاد داشته باشید که دستگاه را به اتاقی اختصاص دهید که یک دستگاه Nest در آن وجود دارد.

2a082ee11d47ad1a.png

3. واشر هوشمند را راه اندازی کنید

اگر « فعال کردن تکمیل محلی برای کد لبه یکپارچه‌سازی ابر به ابر» را اجرا کرده‌اید، باید واشر هوشمند مجازی را قبلاً راه‌اندازی کرده باشید. اگر متوقف شد، به یاد داشته باشید که دستگاه مجازی را مجددا راه اندازی کنید.

دستگاه را راه اندازی کنید

به دایرکتوری virtual-device/ بروید و اسکریپت دستگاه را اجرا کنید و پارامترهای پیکربندی را به عنوان آرگومان ارسال کنید:

$ cd ../../virtual-device
$ npm install
$ npm start -- \
  --deviceId=deviceid123 --projectId=<project-id> \
  --discoveryPortOut=3311 --discoveryPacket=HelloLocalHomeSDK

بررسی کنید که اسکریپت دستگاه با پارامترهای مورد انتظار اجرا شود:

(...): UDP Server listening on 3311
(...): Device listening on port 3388
(...): Report State successful

4. برنامه Local Home را تست کنید

از طریق دستورات صوتی به دستگاه Google Home دستورات را به دستگاه خود ارسال کنید، مانند:

"Hey Google، ماشین لباسشویی من را روشن کن."

"Hey Google، ماشین لباسشویی من را روشن کن."

"Hey Google, اجباری محلی."

"Hey Google، ماشین لباسشویی من را متوقف کن."

متوجه خواهید شد که وقتی می‌خواهید ماشین لباسشویی را بعد از «اجبار محلی» کنترل کنید، «متاسفم، به نظر می‌رسد که واشر معیوب در حال حاضر در دسترس نیست» پاسخ می‌دهد.

این بدان معنی است که دستگاه از طریق یک مسیر محلی قابل دسترسی نیست. قبل از صدور "Hey Google, اجباری محلی" کار می کرد زیرا زمانی که دستگاه از طریق یک مسیر محلی قابل دسترسی نباشد، دوباره به استفاده از مسیر ابری می پردازیم. اما پس از "force local" گزینه بازگشت به مسیر ابری غیرفعال است.

برای اینکه بفهمیم مشکل چیست، بیایید از ابزارهایی که در اختیار داریم استفاده کنیم: متریک‌ها و ثبت‌نام Google Cloud Platform (GCP) و ابزارهای توسعه‌دهنده Chrome.

5. برنامه Local Home را اشکال زدایی کنید

در بخش بعدی، از ابزارهای ارائه شده توسط گوگل استفاده خواهید کرد تا متوجه شوید که چرا دستگاه از طریق مسیر محلی قابل دسترسی نیست. می‌توانید از ابزارهای برنامه‌نویس Google Chrome برای اتصال به دستگاه Google Home، مشاهده گزارش‌های کنسول و اشکال‌زدایی برنامه Local Home استفاده کنید. همچنین می‌توانید گزارش‌های سفارشی را به Cloud Logging ارسال کنید تا بتوانید از بزرگترین خطاهایی که کاربران شما در برنامه Local Home خود پیدا می‌کنند آگاه شوید.

Chrome Developer Tools را وصل کنید

برای اتصال دیباگر به برنامه تکمیل محلی خود، این مراحل را دنبال کنید:

  1. مطمئن شوید که دستگاه Google Home خود را به کاربری با مجوز دسترسی به پروژه Developer Console مرتبط کرده اید.
  2. دستگاه Google Home خود را راه اندازی مجدد کنید، که به آن امکان می دهد URL HTML شما و همچنین پیکربندی اسکنی را که در Developer Console قرار داده اید دریافت کند.
  3. Chrome را در دستگاه توسعه خود راه اندازی کنید.
  4. یک برگه کروم جدید باز کنید و chrome://inspect در قسمت آدرس وارد کنید تا بازرس راه اندازی شود.

باید فهرستی از دستگاه‌ها را در صفحه ببینید و URL برنامه شما باید زیر نام دستگاه Google Home شما ظاهر شود.

567f97789a7d8846.png

بازرس را راه اندازی کنید

برای راه‌اندازی ابزار برنامه‌نویس Chrome، بر روی Inspect در زیر URL برنامه کلیک کنید. تب Console را انتخاب کنید و تأیید کنید که می‌توانید محتوای IDENTIFY را که توسط برنامه TypeScript چاپ شده است ببینید.

774c460c59f9f84a.png

این خروجی به این معنی است که کنترل کننده IDENTIFY با موفقیت راه اندازی شده است، اما verificationId که در IdentifyResponse برگردانده شده است با هیچ یک از دستگاه های HomeGraph شما مطابقت ندارد. بیایید چند گزارش سفارشی اضافه کنیم تا دلیل آن را بفهمیم.

گزارش های سفارشی را اضافه کنید

اگرچه یک خطای DEVICE_VERIFICATION_FAILED توسط Local Home SDK چاپ شده است، اما در یافتن علت اصلی کمک چندانی نمی کند. بیایید چند گزارش سفارشی اضافه کنیم تا مطمئن شویم که داده‌های اسکن را به درستی می‌خوانیم و پردازش می‌کنیم، و توجه داشته باشیم که اگر قول را با خطا رد کنیم، پیام خطا در واقع به Cloud Logging نیز ارسال می‌شود.

local/index.ts

identifyHandler(request: IntentFlow.IdentifyRequest):
    Promise<IntentFlow.IdentifyResponse> {
  console.log("IDENTIFY intent: " + JSON.stringify(request, null, 2));

  const scanData = request.inputs[0].payload.device.udpScanData;
  if (!scanData) {
    const err = new IntentFlow.HandlerError(request.requestId,
        'invalid_request', 'Invalid scan data');
    return Promise.reject(err);
  }

  // In this codelab, the scan data contains only local device id.
  // Is there something wrong here?
  const localDeviceId = Buffer.from(scanData.data);
  console.log(`IDENTIFY handler: received local device id
      ${localDeviceId}`);

  // Add custom logs
  if (!localDeviceId.toString().match(/^deviceid[0-9]{3}$/gi)) {
    const err = new IntentFlow.HandlerError(request.requestId,
        'invalid_device', 'Invalid device id from scan data ' +
        localDeviceId);
    return Promise.reject(err);
  }

  const response: IntentFlow.IdentifyResponse = {
    intent: Intents.IDENTIFY,
    requestId: request.requestId,
    payload: {
      device: {
        id: 'washer',
        verificationId: localDeviceId.toString(),
      }
    }
  };
  console.log("IDENTIFY response: " + JSON.stringify(response, null, 2));

  return Promise.resolve(response);
}

همچنین، نسخه محلی برنامه خانگی را تغییر دهید تا بتوانیم تشخیص دهیم که آیا از نسخه صحیح استفاده می کنیم یا خیر.

local/index.ts

const localHomeSdk = new App('1.0.1');

پس از افزودن گزارش‌های سفارشی، باید دوباره برنامه را کامپایل کرده و مجدداً در Firebase مستقر کنید.

$ cd ../app-faulty/local
$ npm run build
$ firebase deploy --only hosting

اکنون، دستگاه Google Home خود را مجددا راه اندازی کنید تا بتواند برنامه محلی به روز شده را بارگیری کند. با مشاهده گزارش‌های کنسول در ابزارهای برنامه‌نویس Chrome، می‌توانید ببینید که آیا دستگاه Google Home از نسخه مورد انتظار استفاده می‌کند یا خیر.

ecc56508ebcf9ab.png

دسترسی به Cloud Logging

بیایید نحوه استفاده از Cloud Logging برای یافتن خطاهای خود را بررسی کنیم. برای دسترسی به Cloud Logging برای پروژه خود:

  1. در کنسول Cloud Platform، به صفحه Projects بروید.
  2. پروژه خانه هوشمند خود را انتخاب کنید.
  3. در بخش عملیات ، Logging > Logs Explorer را انتخاب کنید.

دسترسی به داده های ورود به سیستم از طریق مدیریت هویت و دسترسی (IAM) برای کاربران پروژه ادغام شما مدیریت می شود. برای جزئیات بیشتر در مورد نقش‌ها و مجوزهای ثبت داده‌ها، به کنترل دسترسی Cloud Logging مراجعه کنید.

از فیلترهای پیشرفته استفاده کنید

می دانیم که خطاهایی در هدف IDENTIFY رخ می دهد، زیرا مسیر محلی کار نمی کند زیرا دستگاه محلی شناسایی نمی شود. با این حال، ما می‌خواهیم دقیقاً بدانیم مشکل چیست، بنابراین اجازه دهید ابتدا خطاهایی را که در کنترل کننده IDENTIFY رخ می‌دهند فیلتر کنیم.

روی دکمه Show query کلیک کنید، باید به کادر سازنده Query تبدیل شود. jsonPayload.intent="IDENTIFY" در کادر Query builder وارد کنید و روی دکمه Run query کلیک کنید.

4c0b9d2828ee2447.png

در نتیجه، تمام لاگ های خطا را دریافت می کنید که در کنترل کننده IDENTIFY پرتاب می شوند. بعد، آخرین خطا را گسترش دهید. errorCode و debugString را که در هنگام رد کردن وعده تنظیم کرده اید را در کنترل کننده IDENTIFY پیدا خواهید کرد.

71f2f156c6887496.png

از debugString می توانیم بگوییم که شناسه دستگاه محلی در قالب مورد انتظار نیست. برنامه Local Home انتظار دارد شناسه دستگاه محلی را به صورت رشته ای دریافت کند که با deviceid و سپس 3 رقم شروع می شود، اما شناسه دستگاه محلی در اینجا یک رشته هگز است.

خطا را برطرف کنید

با بازگشت به کد منبع که در آن شناسه دستگاه محلی را از داده‌های اسکن تجزیه می‌کنیم، متوجه می‌شویم که هنگام تبدیل رشته به بایت، رمزگذاری را ارائه نکرده‌ایم. داده‌های اسکن به‌عنوان یک رشته هگزا دریافت می‌شوند، بنابراین هنگام فراخوانی Buffer.from() hex به عنوان رمزگذاری کاراکتر ارسال کنید.

local/index.ts

identifyHandler(request: IntentFlow.IdentifyRequest):
    Promise<IntentFlow.IdentifyResponse> {
  console.log("IDENTIFY intent: " + JSON.stringify(request, null, 2));

  const scanData = request.inputs[0].payload.device.udpScanData;
  if (!scanData) {
    const err = new IntentFlow.HandlerError(request.requestId,
        'invalid_request', 'Invalid scan data');
    return Promise.reject(err);
  }

  // In this codelab, the scan data contains only local device id.
  const localDeviceId = Buffer.from(scanData.data, 'hex');
  console.log(`IDENTIFY handler: received local device id
      ${localDeviceId}`);

  if (!localDeviceId.toString().match(/^deviceid[0-9]{3}$/gi)) {
    const err = new IntentFlow.HandlerError(request.requestId,
      'invalid_device', 'Invalid device id from scan data ' +
      localDeviceId);
    return Promise.reject(err);
  }

  const response: IntentFlow.IdentifyResponse = {
    intent: Intents.IDENTIFY,
    requestId: request.requestId,
    payload: {
      device: {
        id: 'washer',
        verificationId: localDeviceId.toString(),
      }
    }
  };
  console.log("IDENTIFY response: " + JSON.stringify(response, null, 2));

  return Promise.resolve(response);
}

همچنین، نسخه محلی برنامه خانگی را تغییر دهید تا بتوانیم تشخیص دهیم که آیا از نسخه صحیح استفاده می کنیم یا خیر.

local/index.ts

const localHomeSdk = new App('1.0.2');

پس از رفع خطا، برنامه را کامپایل کرده و مجدداً در Firebase قرار دهید. در app-faulty/local اجرا کنید:

$ npm run build
$ firebase deploy --only hosting

راه حل خود را تست کنید

پس از استقرار، دستگاه Google Home خود را مجددا راه اندازی کنید تا بتواند برنامه محلی به روز شده را بارگیری کند. مطمئن شوید که نسخه محلی برنامه خانگی 1.0.2 است و این بار هیچ خطایی در کنسول ابزار توسعه دهندگان Chrome مشاهده نمی کنید.

c8456f7b5f77f894.png

اکنون می توانید دوباره دستورات را به دستگاه خود ارسال کنید.

"Hey Google, اجباری محلی."

"Hey Google، ماشین لباسشویی من را متوقف کن."

"Hey Google، ماشین لباسشویی من را روشن کن."

...

«Hey Google، پیش‌فرض اجباری».

6. مجموعه تست را برای خانه هوشمند اجرا کنید

پس از تأیید دستگاه خود با استفاده از کنترل‌های لمسی در برنامه Google Home یا از طریق دستورات صوتی، می‌توانید از مجموعه تست خودکار برای خانه هوشمند برای تأیید موارد استفاده بر اساس انواع دستگاه و ویژگی‌های مرتبط با ادغام خود استفاده کنید. مجموعه تست مجموعه‌ای از آزمایش‌ها را برای شناسایی مشکلات موجود در ادغام شما اجرا می‌کند و پیام‌های آموزنده‌ای را برای موارد آزمایشی ناموفق نشان می‌دهد تا پیش از فرو رفتن در گزارش‌های رویداد، اشکال‌زدایی شما را تسریع کند.

مجموعه تست را برای خانه هوشمند اجرا کنید

برای آزمایش ادغام Cloud-to-Cloud توسط Test Suite این دستورالعمل ها را دنبال کنید:

  1. در مرورگر وب خود، مجموعه تست برای خانه هوشمند را باز کنید.
  2. با استفاده از دکمه در گوشه بالا سمت راست وارد Google شوید. این به مجموعه تست اجازه می دهد تا دستورات را مستقیماً به دستیار Google ارسال کند.
  3. در قسمت Project ID ، شناسه پروژه ادغام Cloud-to-Cloud خود را وارد کنید. و سپس برای ادامه روی NEXT کلیک کنید.
  4. در مرحله تنظیمات تست ، باید واشر معیوب خود را در قسمت Devices and Trais مشاهده کنید.
  5. گزینه Test Request Sync را غیرفعال کنید زیرا برنامه شستشوی نمونه رابط کاربری برای افزودن / حذف / تغییر نام واشر ندارد. در یک سیستم تولیدی، هر زمان که کاربر دستگاه‌ها را اضافه/حذف/تغییر نام می‌دهد، باید Request Sync را فعال کنید.
  6. گزینه Local Home SDK را فعال بگذارید زیرا ما مسیرهای محلی و ابری را آزمایش می کنیم.
  7. برای شروع اجرای آزمون روی Next: Test محیط کلیک کنید.

67433d9190fa770e.png

پس از اتمام تست‌ها، متوجه خواهید شد که تست‌های Pause/Resume در مسیر محلی با شکست مواجه می‌شوند در حالی که تست‌های Pause/Resume در مسیر ابری در حال گذراندن هستند.

d1ebd5cfae2a2a47.png

تجزیه و تحلیل پیام خطا

به پیام‌های خطا در موارد آزمایشی ناموفق نگاه کنید. آنها به شما می گویند که وضعیت مورد انتظار برای آن آزمایش چیست و وضعیت واقعی چگونه بوده است. در این مورد، برای «مکث واشر»، حالت مورد انتظار isPaused: true ، اما در حالت واقعی، isPaused: false . به طور مشابه، برای "مکث واشر"، حالت مورد انتظار isPaused: true است، اما در حالت واقعی ما isPaused: false .

6bfd3acef9c16b84.png

از پیام‌های خطا، به نظر می‌رسد که در مسیر محلی، وضعیت isPaused را برعکس تنظیم می‌کنیم.

خطا را شناسایی و رفع کنید

بیایید کد منبع را پیدا کنیم که در آن برنامه Local Home دستور اجرا را به دستگاه ارسال می کند. getDataCommand() تابعی است که توسط executeHandler() فراخوانی می شود تا payload در دستور اجرایی ارسال شده به دستگاه تنظیم کند.

local/index.ts

getDataForCommand(command: string, params: IWasherParams): unknown {
    switch (command) {
        case 'action.devices.commands.OnOff':
            return {
                on: params.on ? true : false
            };
        case 'action.devices.commands.StartStop':
            return {
                isRunning: params.start ? true : false
            };
        case 'action.devices.commands.PauseUnpause':
            return {
                // Is there something wrong here?
                isPaused: params.pause ? false : true
            };
        default:
            console.error('Unknown command', command);
            return {};
    }
}

در واقع ما isPause را در حالت معکوس قرار می دهیم، زمانی که params.pause true است و در غیر این صورت false باید روی true تنظیم شود. بنابراین، بیایید آن را برطرف کنیم.

local/index.ts

getDataForCommand(command: string, params: IWasherParams): unknown {
    switch (command) {
        case 'action.devices.commands.OnOff':
            return {
                on: params.on ? true : false
            };
        case 'action.devices.commands.StartStop':
            return {
                isRunning: params.start ? true : false
            };
        case 'action.devices.commands.PauseUnpause':
            return {
                isPaused: params.pause ? true : false
            };
        default:
            console.error('Unknown command', command);
            return {};
    }
}

نسخه محلی برنامه home را تغییر دهید تا بتوانیم تشخیص دهیم که آیا از نسخه صحیح استفاده می کنیم یا خیر.

local/index.ts

const localHomeSdk = new App('1.0.3');

به یاد داشته باشید که دوباره برنامه را کامپایل کرده و مجدداً در Firebase مستقر کنید. در app-faulty/local اجرا کنید:

$ npm run build
$ firebase deploy --only hosting

اکنون، دستگاه Google Home خود را مجددا راه اندازی کنید تا بتواند برنامه محلی به روز شده را بارگیری کند. مطمئن شوید که نسخه محلی برنامه home 1.0.3 است.

راه حل خود را تست کنید

اکنون، مجموعه تست برای خانه هوشمند را با همان تنظیمات مجدداً اجرا کنید و متوجه خواهید شد که تمام موارد آزمایشی به پایان رسیده است.

b7fc8c5d3c727d8d.png

7. تبریک می گویم

764dbc83b95782a.png

تبریک می گویم! شما با موفقیت یاد گرفتید که چگونه یک برنامه Local Home را از طریق مجموعه تست برای خانه هوشمند و گزارش ابری عیب یابی کنید.

بیشتر بدانید

در اینجا موارد دیگری وجود دارد که می توانید امتحان کنید:

همچنین می‌توانید درباره آزمایش و ارسال یک ادغام برای بررسی، از جمله فرآیند صدور گواهینامه برای انتشار ادغام خود برای کاربران، اطلاعات بیشتری کسب کنید.