در این مستند، میخواهیم یک شبیهساز جدید برای الگوریتمهای توزیعشده که از ساختار TimedIOAtuomata استفاده میکنند ارائه دهیم، دلیل ارائهی یک شبیهساز جدید دو مورد است:
- شبیهسازهای گذشته توسعهی فعالی ندارند، گاها حتی متنباز نیستند و تکنولوژیای که با آن پیادهشده اند نیز deprecated شده و قابل توسعه نیستند.
- در این پروژهی درس قصد داشتید برخی از شبیهسازیهایی انجام دهیم که با شبیهسازهای گذشته ممکن نبود و به همین دلیل شبیهساز جدیدی ساختیم.
برای شبیهسازی به سه المان اصلی نیاز داریم:
- گراف
- شبیهساز
- اتوماتا
گراف مشخص کنندهی گرهها و یالها و ویژگیهایی روی گره یا یالهاست، مثلا کند بودن گره یا یک خط ارتباطی با ویژگیای که روی آن گره یا یال تعریف میشود قابل بیان است، این ویژگیها به صورت یک dict بیان میشوند که باید برای شبیهساز قابل فهم باشد.
شبیهساز نحوهی ارتباطات را با کمک گراف ورودی هندل میکند، مثلا sync یا async بودن، داشتن failure node، ارسال به صورت FIFO بودن یا نبودن و ... بر عهدهی شبیهساز است، همچنین شبیهساز وظیفهی هندلکردن initial state را نیز دارد، مثلا اینکه آیا اتوماتاها UID داشتهباشند یا خیر، وظیفهی شبیهساز است، حتی شبیهساز وظیفهی مشخصکردن توابع قابل اجرا برای اتوماتاها را دارد، مثلا میتواند اجازهی استفاده از توابع random را به اتوماتا ندهد تا اتوماتاهای دترمنیستید فقط قابل اجرا باشند.
اتوماتا چیزی جز یک لیست از تراکنشها نیست، هر تراکنش ۴ مشخصه دارد:
- نام تراکنش که یک رشتهی یکتا است.
- شرط اولیه که یک تابع است که استیت را ورودی میگیرد و باید خروجی true یا false دهد، شرط اولیه قادر به تغییر state نیست.
- تغییر تغییر نیز یک تابع است که استیت کنونی را میگیرد و در خروجی استیت جدید را میدهد.
- خروجی این تابع میتواند یک خروجی به صورت لیست برگرداند که این لیست از دوتاییهای
(id, message)
تشکیل شده و یعنی پیامmessage
را به همسایه با شناسهیid
ارسال کن.
خروجی و تغییر اختیاری است، اگر خروجی نباشد یعنی پیامی به دیگران ارسال نمیشود و اگر تغییر نباشد یعنی حالت عوض نمیشود، همچنین دو تراکنش init
و receive
باید حتما وجود داشتهباشند که شرط اولیه ندارند در عوض تابع تغییر آنها متفاوت است که در ادامه توضیح داده میشود.
در تراکنش init
در تابع تغییر به جای استیت کنونی یک ورودی که از طرف اجراکننده داده میشود وجود دارد، این ورودی به صورت یک دیکشنری است که شامل اطلاعاتی است که از سوی اجراکننده ارسال شدهاست، مثلا شناسهی همسایهها داخل ورودی قرار میگیرد یا در صورت وجود UID، میتوان UID را در آن قرار داد.
در تراکنش receive
تابع تغییر دو ورودی دارد، علاوه بر استیت کنونی، یک دوتایی به صورت (id, message)
نیز موجود است که شناسهی ارسال کنندهی پیام و متن پیام است.
حالت برای اتوماتاها باید به صورت یک dict باشد که مقادیر آن قابلیت تبدیلشدن به json را داشته باشند تا قابلیت ارسال برای بقیه موجود باشد، همچنین دو کلید color
و label
میتواند موجود باشد که برای ویژوالایز کردن گراف و پیامها به کار میآید، همچنین کلید alive
لازم است که نشان دهندهی زنده بودن این اتوماتا است، فرایند شبیهسازی تا زمانی که گرهای alive
باشد ادامه پیدا میکند.
message
های ارسالشده باید رشته باشند.
بعد از تعریف گراف و اتوماتا، المان اصلی شبیهساز است، اجرا کننده اتوماتا و گراف را ورودی گرفته و وظیفهی شبیهسازی کل فرایند را دارد، برای اینکار به ازای هر گره از گراف، یک ماشین فرضی در نظر میگیرد، این ماشین فرضی در حافظهی خود حالت و یک دیکشنری از شناسه به گرهی واقعی همسایهها دارد، سپس به ازای همهی ماشینها تراکنش init را با ورودی دادن لیست شناسهی گرهها صدا میزند، برای الگوریتمهایی که کل توپولوژی را میدانند این لیست میتواند کل گراف باشد و فقط شامل لیست شناسهی همسایهها نباشد. سپس خروجی تغییر را در به عنوان حالت جدید گذاشته و تابع خروجی را صدا میزند و مقدار return آن را به صف ارسال اضافه میکند. بعد از انجام این کار برای همهی گرهها، در هر مرحله براساس محیط شبیهسازی شده (مثلا سینک بودن یا نبودن و ...) تراکنشهای receive یا تراکنشهایی که precondition آنها برقرار است را اجرا میکند.