זהו מדריך הפקודה ns-3 שניתן להפעיל בספק האירוח החינמי של OnWorks באמצעות אחת מתחנות העבודה המקוונות המרובות שלנו, כגון Ubuntu Online, Fedora Online, אמולטור מקוון של Windows או אמולטור מקוון של MAC OS
תָכְנִית:
שֵׁם
ns-3-manual - ns-3 ידני
זה ns-3 מדריך ל. תיעוד ראשי עבור פרויקט ns-3 זמין בחמישה
צורות:
· ns-3 חמצן: תיעוד של ממשקי ה-API הציבוריים של הסימולטור
· הדרכה, מדריך (זֶה מסמך), וספריית דגמים עבור ה האחרון לשחרר ו
פיתוח עץ
· ns-3 ויקי
תוכן
ארגון
פרק זה מתאר את הכלל ns-3 ארגון התוכנה והמתאים
ארגון המדריך הזה.
ns-3 הוא סימולטור רשת לאירועים דיסקרטיים בו נמצאים ליבת הסימולציה והדגמים
מיושם ב-C++. ns-3 בנוי כספרייה שיכולה להיות סטטית או דינמית
מקושר לתוכנית C++ הראשית המגדירה את טופולוגיית הסימולציה ומתחילה את
מַדמֶה. ns-3 גם מייצא כמעט את כל ה-API שלו לפייתון, מה שמאפשר לתוכניות Python לעשות זאת
לייבא מודול "ns3" כמעט באותו אופן כמו ה- ns-3 הספרייה מקושרת על ידי קובצי הפעלה
ב- C ++.
[תמונה] ארגון תוכנה של ns-3.UNIDENT
קוד המקור עבור ns-3 מאורגן בעיקר ב src ספרייה וניתן לתאר
לפי התרשים ב תוכנה ארגון of ns-3. אנחנו נפעל מלמטה
לְמַעלָה; באופן כללי, למודולים יש רק תלות במודולים מתחתיהם באיור.
ראשית אנו מתארים את הליבה של הסימולטור; אותם רכיבים המשותפים לכולם
פרוטוקול, חומרה ומודלים סביבתיים. ליבת הסימולציה מיושמת ב
src/core. מנות הן אובייקטים בסיסיים בסימולטור רשת ומיושמות ב
src/רשת. שני מודולי הסימולציה הללו כשלעצמם נועדו לכלול א
ליבת סימולציה גנרית שיכולה לשמש סוגים שונים של רשתות, לא רק
רשתות מבוססות אינטרנט. המודולים לעיל של ns-3 אינם תלויים ברשת ספציפית
ודגמי מכשירים, המכוסים בחלקים הבאים של מדריך זה.
בנוסף לאמור לעיל ns-3 הליבה, אנו מציגים, גם בחלק הראשוני של
ידני, שני מודולים נוספים המשלימים את ה-API מבוסס הליבה C++. ns-3 תוכניות עשויות
לגשת ישירות לכל ה-API או לעשות שימוש במה שנקרא עוזר API זה מספק
עטיפות נוחות או עטיפה של קריאות API ברמה נמוכה. העובדה ש ns-3 תוכניות
ניתן לכתוב לשני ממשקי API (או שילוב שלהם) הוא היבט בסיסי של
מַדמֶה. אנו גם מתארים כיצד Python נתמך ב ns-3 לפני המעבר לספציפי
מודלים רלוונטיים להדמיית רשת.
שאר המדריך מתמקד בתיעוד הדגמים ובתמיכה
יכולות. החלק הבא מתמקד בשני אובייקטים בסיסיים ב ns-3: ה צומת ו
NetDevice. שני סוגי NetDevice מיוחדים מיועדים לתמוך בשימוש באמולציית רשת
מקרים, ואמולציה מתוארת בהמשך. הפרק הבא מוקדש ל
דגמים הקשורים לאינטרנט, כולל ה-API של sockets המשמשים יישומי אינטרנט. ה
הפרק הבא עוסק ביישומים, והפרק הבא מתאר תמיכה נוספת
לסימולציה, כגון אנימטורים וסטטיסטיקה.
הפרויקט מחזיק מדריך נפרד המוקדש לבדיקה ואימות של ns-3 קוד
(ראה ns-3 בדיקות ו בדיקת מערכות מדריך ל).
אַקרַאִי משתנים
ns-3 מכיל מחולל מספרים פסאודו-אקראיים (PRNG) מובנה. זה חשוב עבור
משתמשים רציניים בסימולטור כדי להבין את הפונקציונליות, התצורה והשימוש
של PRNG זה, ולהחליט אם זה מספיק לשימוש המחקרי שלו.
מָהִיר סקירה כללית
ns-3 מספרים אקראיים מסופקים באמצעות מופעים של ns3::RandomVariableStream.
· כברירת מחדל, ns-3 סימולציות משתמשות בזרע קבוע; אם יש אקראיות כלשהי ב
סימולציה, כל הפעלה של התוכנית תניב תוצאות זהות אלא אם כן ה-Seed ו/או
מספר הריצה משתנה.
· ב ns-3.3 ומוקדם יותר, ns-3 סימולציות השתמשו בזרע אקראי כברירת מחדל; זה מסמן א
שינוי במדיניות החל מ ns-3.4.
· ב ns-3.14 ומוקדם יותר, ns-3 סימולציות השתמשו במחלקת עטיפה אחרת בשם
ns3::RandomVariable. החל מ ns-3.15, מחלקה זו הוחלף ב
ns3::RandomVariableStream; מחולל המספרים הפסאודו-אקראיים הבסיסיים לא
השתנה.
· כדי להשיג אקראיות על פני ריצות סימולציה מרובות, עליך להגדיר את ה-seed
אחרת או להגדיר את מספר הריצה בצורה שונה. כדי להגדיר זרע, התקשר
ns3::RngSeedManager::SetSeed() בתחילת התוכנית; כדי להגדיר איתו מספר ריצה
אותו זרע, התקשר ns3::RngSeedManager::SetRun() בתחילת התוכנית; לִרְאוֹת
יוצרים אקראי משתנים.
· כל RandomVariableStream בשימוש ns-3 משויך מחולל מספרים אקראיים וירטואלי
עם זה; כל המשתנים האקראיים משתמשים בזרע קבוע או אקראי המבוסס על השימוש ב-
זרע גלובלי (כדור קודם);
· אם אתה מתכוון לבצע ריצות מרובות של אותו תרחיש, באקראי שונה
מספרים, אנא הקפד לקרוא את הסעיף כיצד לבצע שכפולים עצמאיים:
יוצרים אקראי משתנים.
קרא עוד להסבר נוסף על מתקן המספרים האקראיים עבור ns-3.
רקע
סימולציות משתמשות בהרבה מספרים אקראיים; מחקר אחד מצא שרוב סימולציות הרשת
להוציא עד 50% מהמעבד ביצירת מספרים אקראיים. משתמשי סימולציה צריכים להיות
עוסק באיכות המספרים האקראיים (פסאודו) ובאי תלות ביניהם
זרמים שונים של מספרים אקראיים.
משתמשים צריכים להיות מודאגים מכמה בעיות, כגון:
· זריעה של מחולל המספרים האקראיים והאם תוצאת סימולציה היא
דטרמיניסטי או לא,
· כיצד לרכוש זרמים שונים של מספרים אקראיים שאינם תלויים באחד
אחר, ו
· כמה זמן לוקח לזרמים להסתובב
נציג כאן כמה מונחים: RNG מספק רצף ארוך של (פסאודו) אקראי
מספרים. אורכו של רצף זה נקרא מחזור אורך or תקופהלאחר מכן
ה-RNG יחזור על עצמו. ניתן לחלק את הרצף הזה למנותק זרמים.
זרם של RNG הוא תת-קבוצה או בלוק רציף של רצף ה-RNG. למשל, אם ה
תקופת RNG היא באורך N, ושני זרמים מסופקים מה-RNG הזה, ולאחר מכן הראשון
הזרם עשוי להשתמש בערכי ה-N/2 הראשון והזרם השני עשוי לייצר את ה-N/2 השני
ערכים. תכונה חשובה כאן היא ששני הזרמים אינם מתואמים. כְּמוֹ כֵן,
ניתן לחלק כל זרם בצורה לא משולבת למספר זרמים שאינם מתואמים תת-זרמים. ה
הבסיס RNG מקווה מייצר רצף פסאודו אקראי של מספרים עם ארוך מאוד
אורך מחזור, ומחלק את זה לזרמים ותתי-זרמים בצורה יעילה.
ns-3 משתמש באותו מחולל מספרים אקראיים כמו שעושה ns-2: ה-MRG32k3a
גנרטור מבית פייר ל'אקואר. תיאור מפורט ניתן למצוא ב
http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf. מחולל MRG32k3a
מספק זרמים בלתי תלויים של 1.8x10^{19} של מספרים אקראיים, שכל אחד מהם מורכב מ
תת-זרמים בגודל 2.3x10^{15}. לכל תת זרם יש תקופה (כלומר, מספר המספרים האקראיים
לפני חפיפה) של 7.6x10^{22}. התקופה של המחולל כולו היא 3.1x10^{57}.
כיתה ns3::RandomVariableStream הוא הממשק הציבורי למספר האקראי הבסיסי הזה
גֵנֵרָטוֹר. כאשר משתמשים יוצרים משתנים אקראיים חדשים (כגון ns3::UniformRandomVariable,
ns3::ExponentialRandomVariableוכו'), הם יוצרים אובייקט שמשתמש באחד מה-
זרמים נפרדים ובלתי תלויים של מחולל המספרים האקראיים. לכן, כל אובייקט של
סוג ns3::RandomVariableStream יש, מבחינה רעיונית, RNG "וירטואלי" משלו. יתר על כן,
כל אחד ns3::RandomVariableStream ניתן להגדיר להשתמש באחד מקבוצת המשנה שצוירו
מהזרם המרכזי.
יישום חלופי יהיה לאפשר לכל RandomVariable להיות משלו
(בזרעים שונה) RNG. עם זאת, אנחנו לא יכולים להבטיח באותה חזקה שהשונה
רצפים לא יהיו מתואמים במקרה כזה; לפיכך, אנו מעדיפים להשתמש ב-RNG יחיד ו
זרמים ותתי זרמים ממנו.
יוצרים אקראי משתנים
ns-3 תומך במספר אובייקטים משתנים אקראיים ממחלקת הבסיס
RandomVariableStream. חפצים אלו נובעים מ ns3::Object ומטופלים על ידי חכם
מצביעים.
הדרך הנכונה ליצור אובייקטים אלה היא להשתמש בתבנית CreateObject<> שיטה,
כגון:
Ptr x = CreateObject ();
לאחר מכן תוכל לגשת לערכים על ידי קריאה לשיטות על האובייקט כגון:
myRandomNo = x->GetInteger ();
אם תנסה במקום זאת לעשות משהו כזה:
myRandomNo = UniformRandomVariable().GetInteger ();
התוכנית שלך תיתקל בתקלת פילוח, מכיוון שהיישום מסתמך על
בניית תכונה כלשהי שמתרחשת רק כאשר CreateObject נקרא.
חלק גדול משאר פרק זה דן כעת בתכונותיו של זרם
מספרים פסאודו אקראיים שנוצרו מאובייקטים כאלה, וכיצד לשלוט בזריעה של כאלה
חפצים.
זריעה ו עצמאי רפליקציות
ns-3 ניתן להגדיר סימולציות כדי לייצר תוצאות דטרמיניסטיות או אקראיות. אם ה
ns-3 סימולציה מוגדרת להשתמש בזרע קבוע ודטרמיניסטי עם אותו מספר ריצה,
זה אמור לתת את אותו פלט בכל פעם שהוא מופעל.
כברירת מחדל, ns-3 סימולציות משתמשות במספר סיד וריצה קבוע. ערכים אלה מאוחסנים ב
שתיים ns3::GlobalValue מקרים: g_rngSeed ו g_rngRun.
מקרה שימוש טיפוסי הוא להפעיל סימולציה כרצף של ניסויים עצמאיים, כדי לעשות זאת
לחשב סטטיסטיקה על מספר רב של ריצות עצמאיות. המשתמש יכול לשנות את
זריעה גלובלית ולהריץ מחדש את הסימולציה, או יכול לקדם את מצב המשנה של ה-RNG, אשר
מכונה הגדלה של מספר הריצה.
כיתה ns3::RngSeedManager מספק API לשליטה במספר הזריעה וההרצה
התנהגות. יש לקרוא להגדרת מצב זריעה ותת זרם זו לפני כל אקראי
נוצרים משתנים; לְמָשָׁל:
RngSeedManager::SetSeed (3); // משנה את ה-seed מברירת המחדל של 1 ל-3
RngSeedManager::SetRun (7); // משנה את מספר הריצה מברירת המחדל של 1 ל-7
// כעת, צור משתנים אקראיים
Ptr x = CreateObject ();
Ptr y = CreateObject ();
...
מה עדיף, להגדיר זרע חדש או לקדם את מצב המשנה? אין
להבטיח שהזרמים המיוצרים על ידי שני זרעים אקראיים לא יחפפו. הדרך היחידה לעשות זאת
להבטיח ששני זרמים אינם חופפים זה להשתמש ביכולת המשנה שמסופקת על ידי
יישום RNG. לכן, להשתמש מה היא תת זרם יכולת ל לייצר מספר
עצמאי פועל of מה היא אותו סימולציה. במילים אחרות, יותר קפדני מבחינה סטטיסטית
הדרך להגדיר מספר רב של שכפולים עצמאיים היא להשתמש ב-Seed קבוע ולהתקדם
מספר הריצה. יישום זה מאפשר לכל היותר 2.3x10^{15} עצמאיים
שכפולים באמצעות תתי הזרמים.
לנוחות השימוש, אין צורך לשלוט במספר הזרע והריצה מתוך ה-
תכנית; המשתמש יכול להגדיר את NS_GLOBAL_VALUE משתנה סביבה כדלקמן:
$ NS_GLOBAL_VALUE="RngRun=3" ./waf --הפעל שם תוכנית
דרך נוספת לשלוט בכך היא על ידי העברת ארגומנט שורת פקודה; מכיוון שזהו an ns-3
בדוגמה של GlobalValue, זה נעשה באופן שווה כמו הבא:
$ ./waf --command-template="%s --RngRun=3" --הפעל שם תוכנית
או, אם אתה מריץ תוכניות ישירות מחוץ ל-waf:
$ ./build/optimized/scratch/program-name --RngRun=3
גרסאות שורת הפקודה לעיל מקלות על הפעלת המון ריצות שונות מתוך מעטפת
סקריפט על ידי העברת אינדקס RngRun אחר.
כיתה RandomVariableStream
כל המשתנים האקראיים צריכים לנבוע מכיתה משתנה רנדומלי. מחלקה בסיס זו מספקת א
כמה שיטות לקביעת תצורה גלובלית של ההתנהגות של מחולל המספרים האקראיים. נִגזָר
מחלקות מספקות API לציור שינויים אקראיים מהישות ההפצה המסוימת
נתמך.
כל RandomVariableStream שנוצר בסימולציה מקבל מחולל שהוא חדש
RNNGStream מה-PRNG הבסיסי. בשימוש בצורה זו, יישום L'Ecuyer
מאפשר מקסימום 1.8x10^19 משתנים אקראיים. כל משתנה אקראי ביחיד
שכפול יכול לייצר עד 7.6x10^22 מספרים אקראיים לפני חפיפה.
בסיס בכיתה ציבורי API
להלן מובאות מספר שיטות ציבוריות לשיעור RandomVariableStream הגישה ל
הערך הבא בתת-הזרם.
/ **
* \brief מחזירה כפול אקראי מההתפלגות הבסיסית
* \return ערך אקראי של נקודה צפה
*/
כפול GetValue (בטל) const;
/ **
* \brief מחזירה מספר שלם אקראי מההתפלגות הבסיסית
* \return שחקנים שלם של ::GetValue()
*/
uint32_t GetInteger (void) const;
כבר תיארנו את תצורת הזריעה לעיל. משתנה אקראי שונה
תת-מחלקות עשויות להיות עם API נוסף.
סוגים of משתנים אקראיים
הסוגים הבאים של משתנים אקראיים מסופקים, ומתועדים ב- ns-3
דוקסגן או על ידי קריאה src/core/model/random-variable-stream.h. משתמשים יכולים גם ליצור
משתנים אקראיים מותאמים אישית משלהם על ידי גזירה מכיתה RandomVariableStream.
· מעמד UniformRandomVariable
· מעמד ConstantRandomVariable
· מעמד SequentialRandomVariable
· מעמד משתנה אקספוננציאלי
· מעמד ParetoRandomVariable
· מעמד WeibullRandomVariable
· מעמד NormalRandomVariable
· מעמד LogNormalRandomVariable
· מעמד GammaRandomVariable
· מעמד ErlangRandomVariable
· מעמד TriangularRandomVariable
· מעמד ZipfRandomVariable
· מעמד ZetaRandomVariable
· מעמד DeterministicRandomVariable
· מעמד EmpiricalRandomVariable
סמנטיקה of RandomVariableStream אובייקטים
אובייקטים RandomVariableStream נובעים מ ns3::Object ומטופלים על ידי מצביעים חכמים.
ניתן להשתמש במופעי RandomVariableStream גם ב ns-3 תכונות, כלומר
ניתן להגדיר עבורם ערכים באמצעות ה ns-3 מערכת תכונות. דוגמה היא ב
מודלים להפצה עבור WifiNetDevice:
TypeId
RandomPropagationDelayModel::GetTypeId (בטל)
{
static TypeId tid = TypeId ("ns3::RandomPropagationDelayModel")
.SetParent ()
.AddConstructor ()
.AddAttribute ("משתנה",
"המשתנה המקרי שיוצר עיכובים אקראיים(ים).",
StringValue ("ns3::UniformRandomVariable"),
MakePointerAccessor (&RandomPropagationDelayModel::m_variable),
MakePointerChecker ())
;
לחזור tid;
}
הנה ה ns-3 המשתמש יכול לשנות את משתנה האקראי המוגדר כברירת מחדל עבור מודל השהיה זה (שהוא
UniformRandomVariable הנע בין 0 ל-1) דרך מערכת התכונות.
שימוש אַחֵר PRNG
אין כרגע תמיכה בהחלפת מספר אקראי בסיסי אחר
מחולל (למשל, הספרייה המדעית של גנו או חבילת Akaroa). תיקונים יתקבלו בברכה.
הגדרת מה היא זרם מספר
המחולל הבסיסי MRG32k3a מספק 2^64 זרמים עצמאיים. ב-ns-3, אלה הם
מוקצה ברצף החל מהזרם הראשון כמופעי RandomVariableStream חדשים
לבצע את הקריאה הראשונה שלהם ל-GetValue().
כתוצאה מהאופן שבו אובייקטים RandomVariableStream אלה מוקצים לזרמים הבסיסיים,
ההקצאה רגישה להפרעות של תצורת הסימולציה. ה
התוצאה היא שאם היבט כלשהו של תצורת הסימולציה משתנה, המיפוי
של משתנים אקראיים לזרמים עשויים (או לא) להשתנות.
כדוגמה קונקרטית, משתמש המריץ מחקר השוואתי בין פרוטוקולי ניתוב עשוי
מצא שהפעולה של שינוי פרוטוקול ניתוב אחד עבור אחר תבחין כי
גם דפוס הניידות הבסיסי השתנה.
החל מ-ns-3.15, ניתנה שליטה מסוימת למשתמשים כדי לאפשר למשתמשים לעשות זאת
אופציונלי לתקן את ההקצאה של אובייקטי RandomVariableStream שנבחרו לבסיס
זרמים. זה זרם תכונה, חלק ממחלקת הבסיס RandomVariableStream.
על ידי חלוקת רצף הזרמים הקיים מלפני:
<------------------------------------------------- -------------------------->
זרם 0 זרם (2^64 - 1)
לשתי ערכות שוות בגודלן:
<------------------------------------------------- -------------------------->
^ ^^ ^
| || |
זרם 0 זרם (2^63 - 1) זרם 2^63 זרם (2^64 - 1)
<- מוקצה אוטומטית -----------><- מוקצה על ידי משתמש ------------------>
הזרמים הראשונים של 2^63 ממשיכים להיות מוקצים אוטומטית, בעוד ש-2^63 האחרונים כן
נתון מדדי זרם שמתחילים באפס עד 2^63-1.
הקצאת זרמים למספר זרם קבוע היא אופציונלית; מקרים של
RandomVariableStream שלא הוקצה להם ערך זרם יוקצה בפעם הבאה
אחד ממאגר הזרמים האוטומטיים.
כדי לתקן RandomVariableStream לזרם בסיס מסוים, הקצה אותו זרם
תכונה למספר שלם לא שלילי (ערך ברירת המחדל של -1 אומר שערך יהיה
מוקצה אוטומטית).
הוצאה לאור שֶׁלְךָ תוצאות
כאשר אתה מפרסם תוצאות סימולציה, נתון מפתח של מידע תצורה שאתה
צריך תמיד לציין כיצד השתמשת במחולל המספרים האקראיים.
· באילו זרעים השתמשת,
· באיזה RNG השתמשת אם לא ברירת המחדל,
· כיצד בוצעו ריצות עצמאיות,
· לסימולציות גדולות איך בדקת שלא רכבת.
חובה על החוקר המפרסם תוצאות לכלול מספיק מידע
לאפשר לאחרים לשחזר את התוצאות שלו. כמו כן, מוטל על החוקר
לשכנע את עצמך שהמספרים האקראיים שבהם נעשה שימוש היו תקפים סטטיסטית, ולציין זאת
העיתון מדוע מניחים אמון כזה.
<br> סיכום
בואו נסקור אילו דברים עליכם לעשות בעת יצירת סימולציה.
· החליטו אם אתם רצים עם זרע קבוע או זרע אקראי; זרע קבוע הוא
ברירת מחדל
· החליטו כיצד אתם עומדים לנהל רפליקציות עצמאיות, אם ישים,
· שכנע את עצמך שאתה לא מצייר ערכים אקראיים יותר מאורך המחזור, אם
אתה מפעיל סימולציה ארוכה מאוד, ו
· כשאתה מפרסם, פעל לפי ההנחיות לעיל לגבי תיעוד השימוש שלך באקראי
מחולל מספרים.
בליל פונקציות
ns-3 מספק ממשק גנרי לפונקציות hash למטרות כלליות. בצורה הכי פשוטה
שימוש, פונקציית ה-hash מחזירה את ה-hash של 32 סיביות או 64 סיביות של מאגר נתונים או מחרוזת.
ברירת המחדל של פונקציית ה-hash הבסיסית היא ממלמל 3, נבחר כי יש לו פונקציית Hash טובה
מאפיינים ומציע גרסת 64 סיביות. המכובד FNV1a hash זמין גם.
יש מנגנון פשוט להוסיף (או לספק בזמן ריצה) hash חלופי
יישומי פונקציות.
בסיסי נוֹהָג
הדרך הפשוטה ביותר לקבל ערך hash של מאגר נתונים או מחרוזת היא פשוט:
#include "ns3/hash.h"
שימוש במרחב שמות ns3;
char * חיץ = ...
size_t buffer_size = ...
uint32_t buffer_hash = Hash32 (buffer, buffer_size);
std::string s;
uint32_t string_hash = Hash32 (s);
פונקציות שוות מוגדרות עבור ערכי גיבוב של 64 סיביות.
מצטבר חבטות
במצבים מסוימים כדאי לחשב את ה-hash של מספר מאגרים, כאילו יש להם
חוברו יחדיו. (לדוגמה, ייתכן שתרצה את ה-hash של זרם מנות, אבל לא
רוצה להרכיב מאגר בודד עם התוכן המשולב של כל החבילות.)
זה פשוט כמעט כמו הדוגמה הראשונה:
#include "ns3/hash.h"
שימוש במרחב שמות ns3;
char * חיץ;
size_t buffer_size;
האשר האשר; // השתמש בפונקציית גיבוב ברירת המחדל
ל ( )
{
buffer = get_next_buffer ();
hasher (buffer, buffer_size);
}
uint32_t combined_hash = hasher.GetHash32 ();
כברירת מחדל האשר שומר על מצב פנימי כדי לאפשר גיבוב מצטבר. אם אתה רוצה
שימוש חוזר א האשר אובייקט (לדוגמה מכיוון שהוא מוגדר עם hash שאינו ברירת מחדל
function), אבל לא רוצה להוסיף ל-hash שחושב קודם לכן, אתה צריך ברור()
ראשון:
hasher.clear ().GetHash32 (buffer, buffer_size);
זה מאתחל מחדש את המצב הפנימי לפני הגיבוב של המאגר.
שימוש an חלופה בליל פונקציה
פונקציית הגיבוב המוגדרת כברירת מחדל היא ממלמל 3. FNV1a זמין גם. כדי לציין את ה-hash
פונקציה מפורשת, השתמש במעצב זה:
Hasher hasher = Hasher ( צור () );
מוסיף חדש בליל פונקציה יישומים
כדי להוסיף את פונקציית ה-hash Foo, עקוב אחרי hash-murmur3.h/. DC תַבְנִית:
· צור הצהרת כיתה (.h) והגדרה (. DC) יורשת מ
Hash::יישום.
· לכלול ההכרזה ב hash.h (בנקודה שבה hash-murmur3.h כלול.
· בקוד משלך, בצע מופע א האשר אובייקט דרך הקונסטרוקטור האשר
(Ptr ())
אם פונקציית ה-hash שלך היא פונקציה בודדת, למשל hashf, אתה אפילו לא צריך ליצור א
מחלקה חדשה שנגזרת מ- HashImplementation:
Hasher hasher =
האשר (צור (&hashf) );
כדי שזה יקמפל, שלך hashf צריך להתאים לאחת מחתימות מצביע הפונקציות:
typedef uint32_t (*Hash32Function_ptr) (const char *, const size_t);
typedef uint64_t (*Hash64Function_ptr) (const char *, const size_t);
מקורות ל בליל פונקציות
מקורות ליישומי פונקציית Hash אחרים כוללים:
· פיטר קנקובסקי: http://www.strchr.com
· ארש פרטו: http://www.partow.net/programming/hashfunctions/index.html
· SMHasher: http://code.google.com/p/smhasher/
· Sanmayce: http://www.sanmayce.com/Fastest_Hash/index.html
אירועים ו מדמה
ns-3 הוא סימולטור רשת לאירועים דיסקרטיים. מבחינה קונספטואלית, הסימולטור עוקב אחר א
מספר אירועים שמתוכננים לביצוע בזמן סימולציה מוגדר. העבודה של
הסימולטור אמור לבצע את האירועים בסדר זמן רציף. לאחר השלמת
מתרחש אירוע, הסימולטור יעבור לאירוע הבא (או ייצא אם אין
אירועים נוספים בתור האירועים). אם, למשל, אירוע מתוזמן לזמן סימולציה
"100 שניות" מבוצע, והאירוע הבא לא מתוזמן עד "200 שניות",
סימולטור יקפוץ מיד מ-100 שניות ל-200 שניות (של זמן סימולציה) ל
לבצע את האירוע הבא. זה הכוונה בסימולטור "אירוע דיסקרטי".
כדי לגרום לכל זה לקרות, הסימולטור צריך כמה דברים:
1. אובייקט סימולטור שיכול לגשת לתור אירועים שבו מאוחסנים אירועים וזה יכול
לנהל את ביצוע האירועים
2. מתזמן אחראי להכנסת והסרה של אירועים מהתור
3. דרך לייצג זמן סימולציה
4. האירועים עצמם
פרק זה של המדריך מתאר את האובייקטים הבסיסיים הללו (סימולטור, מתזמן,
זמן, אירוע) וכיצד הם משמשים.
אירוע
ל be נשלם
מדמה
כיתת הסימולטור היא נקודת הכניסה הציבורית לגישה למתקני תזמון אירועים. פַּעַם
מספר אירועים נקבעו כדי להתחיל את הסימולציה, המשתמש יכול להתחיל
בצע אותם על ידי כניסה ללולאה הראשית של הסימולטור (התקשר סימולטור::Run). פעם הלולאה הראשית
מתחיל לפעול, הוא יבצע ברצף את כל האירועים המתוזמנים בסדר מהישן ביותר עד
העדכני ביותר עד שלא נותרו אירועים נוספים בתור האירועים או
סימולטור::עצור נקרא.
כדי לתזמן אירועים לביצוע על ידי הלולאה הראשית של הסימולטור, מחלקת הסימולטור מספקת
הסימולטור::Schedule* משפחת הפונקציות.
1. טיפול במטפלי אירועים עם חתימות שונות
פונקציות אלו מוכרזות ומיושמות כתבניות C++ לטיפול אוטומטי ב
מגוון רחב של חתימות C++ של מטפל באירועים בשימוש בטבע. לדוגמה, כדי לתזמן א
אירוע לביצוע 10 שניות בעתיד, ולהפעיל שיטה או פונקציה C++
טיעונים ספציפיים, אתה יכול לכתוב את זה:
מטפל ריק (int arg0, int arg1)
{
std::cout << "handler נקרא עם ארגומנט arg0=" << arg0 << " ו
arg1=" << arg1 << std::endl;
}
סימולטור::Schedule(שניות(10), &handler, 10, 5);
מה יפיק:
המטפל נקרא עם ארגומנט arg0=10 ו-arg1=5
כמובן, תבניות C++ אלו יכולות להתמודד גם עם שיטות חבר בשקיפות ב-C++
חפצים you
ל be הושלם: חבר שיטה דוגמה
הערות:
· שיטות ns-3 Schedule מזהות אוטומטית פונקציות ושיטות רק אם הן
קח פחות מ-5 טיעונים. אם אתה צריך אותם כדי לתמוך בטיעונים נוספים, בבקשה, הגש א
דוח שגיאות.
· קוראים המכירים את המונח 'פונקציות הקשורות במלואן' יזהו את
סימולטור::תזמון שיטות כדרך לבנייה אוטומטית של אובייקטים כאלה.
2. פעולות תזמון נפוצות
ה-API של סימולטור תוכנן כדי להפוך את זה ממש פשוט לתזמן את רוב האירועים. זה
מספק שלוש גרסאות לעשות זאת (בסדר מהשימוש הנפוץ ביותר לפחות נפוץ):
· שיטות תזמון המאפשרות לך לתזמן אירוע בעתיד על ידי מתן
עיכוב בין זמן הסימולציה הנוכחי לתאריך התפוגה של אירוע היעד.
· שיטות ScheduleNow המאפשרות לך לתזמן אירוע לסימולציה הנוכחית
זמן: הם יבצעו _אחרי_ שהאירוע הנוכחי יסתיים ביצוע אבל _לפני_
זמן הסימולציה משתנה לאירוע הבא.
· שיטות ScheduleDestroy המאפשרות לך להתחבר לתהליך הכיבוי של הסימולטור
לניקוי משאבי סימולציה: כל אירוע 'הרס' מבוצע כאשר המשתמש מתקשר
שיטת Simulator::Destroy.
3. שמירה על הקשר הסימולציה
ישנן שתי דרכים בסיסיות לתזמן אירועים, עם ובלי הקשר. מה זה
אומר?
Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj);
לעומת
סימולטור::ScheduleWithContext (uint32_t context, Time const &time, MEM mem_ptr, OBJ obj);
קוראים שמשקיעים זמן ומאמץ בפיתוח או שימוש במודל סימולציה לא טריוויאלי
יידע את הערך של מסגרת הרישום ns-3 לניפוי באגים בסימולציות פשוטות ומורכבות
דוֹמֶה. אחת התכונות החשובות שמספקת מסגרת רישום זו היא
תצוגה אוטומטית של מזהה צומת הרשת המשויך לאירוע הפועל 'כרגע'.
זיהוי הצומת של צומת הרשת הפועל כעת נמצא למעשה במעקב על ידי הסימולטור
מעמד. ניתן לגשת אליו באמצעות שיטת Simulator::GetContext אשר מחזירה את
'הקשר' (מספר שלם של 32 סיביות) המשויך ומאוחסן באירוע המתבצע כעת. ב
כמה מקרים נדירים, כאשר אירוע אינו משויך לצומת רשת ספציפי, שלו
'הקשר' מוגדר ל-0xffffffff.
כדי לשייך הקשר לכל אירוע, השיטות Schedule ו-ScheduleNow באופן אוטומטי
השתמש מחדש בהקשר של האירוע המתבצע כעת כהקשר של האירוע המתוזמן
לביצוע מאוחר יותר.
במקרים מסוימים, בעיקר כאשר מדמים שידור של חבילה מצומת אל
אחר, התנהגות זו אינה רצויה שכן ההקשר הצפוי של אירוע הקבלה הוא
זה של הצומת המקבל, לא הצומת השולח. כדי למנוע בעיה זו, הסימולטור
class מספקת שיטת לוח זמנים ספציפית: ScheduleWithContext המאפשרת לספק
במפורש מזהה הצומת של הצומת המקבל המשויך לאירוע הקבלה.
XXX: קוד דוגמה
במקרים נדירים מאוד, ייתכן שהמפתחים יצטרכו לשנות או להבין כיצד ההקשר
(מזהה צומת) של האירוע הראשון מוגדר לזה של הצומת המשויך לו. זה מושג
לפי המחלקה NodeList: בכל פעם שנוצר צומת חדש, המחלקה NodeList משתמשת
ScheduleWithContext לתזמן אירוע 'אתחול' עבור הצומת הזה. אירוע 'אתחול'
כך מבצע עם הקשר מוגדר לזה של מזהה הצומת ויכול להשתמש במגוון הרגיל של
שיטות לוח זמנים. זה מפעיל את שיטת Node::initialize שמפיצה את ה-'initialize'
אירוע על ידי קריאה למתודה DoInitialize עבור כל אובייקט המשויך לצומת. ה
שיטת DoInitialize נדחתה בחלק מהאובייקטים הללו (בעיקר ביישום
base class) יתזמן כמה אירועים (בעיקר Application::StartApplication) אשר
יתזמן בתורו אירועי יצירת תנועה אשר בתורם יתזמנו
אירועים ברמת הרשת.
הערות:
· משתמשים צריכים להקפיד להפיץ שיטות DoInitialize על פני אובייקטים על ידי קריאה
אתחול במפורש על האובייקטים החברים שלהם
· לזהות ההקשר המשויך לכל שיטת ScheduleWithContext יש שימושים נוספים מעבר לכך
רישום: הוא משמש ענף ניסיוני של ns-3 לביצוע סימולציה מקבילה
מערכות מרובות ליבות המשתמשות ב-multithreading.
הפונקציות של סימולטור::* אינן יודעות מהו ההקשר: הן רק מוודאות את זה
כל ההקשר שתציין עם ScheduleWithContext זמין כאשר המתאים
אירוע מבוצע עם ::GetContext.
זה תלוי במודלים המיושמים על גבי Simulator::* לפרש את ערך ההקשר.
ב-ns-3, דגמי הרשת מפרשים את ההקשר כמזהה הצומת של הצומת אשר
יצר אירוע. זו הסיבה שחשוב להתקשר ל-ScheduleWithContext
ns3:: תת-מחלקות ערוץ מכיוון שאנו מייצרים אירוע מהצומת i לצומת j ואנחנו
רוצה לוודא שלאירוע שיפעל על node j יש את ההקשר הנכון.
זְמַן
ל be נשלם
מתזמן
ל be נשלם
התקשרות חוזרת
כמה משתמשים חדשים ל ns-3 אינם מכירים ביטוי תכנות בשימוש נרחב
לאורך הקוד: ה ns-3 תתקשר בחזרה. פרק זה מספק קצת מוטיבציה בנושא
התקשרות חוזרת, הנחיות כיצד להשתמש בו ופרטים על יישומו.
התקשרות חוזרת מוטיבציה
קחו בחשבון שיש לכם שני דגמי סימולציה A ו-B, ואתם רוצים שהם יעברו
מידע ביניהם במהלך הסימולציה. אחת הדרכים שאתה יכול לעשות זאת היא שאתה
יכולים לגרום ל-A ו-B כל אחד להיות בעל ידע מפורש לגבי השני, כך שהם יכולים להפעיל
שיטות אחד על השני:
כיתה א {
פּוּמְבֵּי:
void ReceiveInput ( // פרמטרים);
...
}
(בקובץ מקור אחר:)
מחלקה ב' {
פּוּמְבֵּי:
void DoSomething (בטל);
...
פרטי you
A* a_instance; // מצביע על A
}
לבטל את
B::DoSomething()
{
// ספר ל_מופע שמשהו קרה
a_instance->ReceiveInput ( // פרמטרים);
...
}
זה בהחלט עובד, אבל יש לו החיסרון שהוא מציג תלות ב-A ו-B
לדעת על האחר בזמן הקומפילציה (זה מקשה על להיות עצמאי
יחידות הידור בסימולטור) ואינה מוכללת; אם בתרחיש שימוש מאוחר יותר,
B צריך לדבר עם אובייקט C שונה לחלוטין, קוד המקור של B צריך להיות
שונה כדי להוסיף א c_instance וכן הלאה. קל לראות שמדובר בכוח גס
מנגנון תקשורת שיכול להוביל לקיומת תכנות במודלים.
זה לא אומר שחפצים לא צריכים לדעת אחד על השני אם יש קושי
תלות ביניהם, אבל שלעתים קרובות ניתן להפוך את המודל לגמיש יותר אם זה
אינטראקציות מוגבלות פחות בזמן ההידור.
זו לא בעיה מופשטת עבור מחקר הדמיית רשת, אלא היא הייתה
מקור לבעיות בסימולטורים קודמים, כאשר חוקרים רוצים להרחיב או לשנות את
מערכת לעשות דברים שונים (כפי שהם נוהגים לעשות במחקר). קחו למשל,
משתמש שרוצה להוסיף שכבת משנה של פרוטוקול אבטחה IPsec בין TCP ל-IP:
------------ -----------
| TCP | | TCP |
------------ -----------
| הופך -> |
---------- -----------
| IP | | IPsec |
---------- -----------
|
-----------
| IP |
-----------
אם הסימולטור עשה הנחות, וקידד בקשיחה לקוד, ה-IP הזה תמיד מדבר
לפרוטוקול תחבורה לעיל, המשתמש עלול להיאלץ לפרוץ למערכת כדי לקבל את
חיבורים רצויים. ברור שזו לא דרך אופטימלית לעצב גנרי
מַדמֶה.
התקשרות חוזרת רקע
הערה:
קוראים המכירים בתכנות התקשרויות חוזרות עשויים לדלג על קטע הדרכה זה.
המנגנון הבסיסי המאפשר לטפל בבעיה לעיל ידוע בשם א תתקשר בחזרה.
המטרה הסופית היא לאפשר לפיסת קוד אחת לקרוא לפונקציה (או שיטה ב-C++)
ללא תלות ספציפית בין מודול.
זה אומר בסופו של דבר שאתה צריך סוג של עקיפה -- אתה מתייחס לכתובת של
נקרא פונקציה כמשתנה. משתנה זה נקרא משתנה מצביע לפונקציה.
היחס בין פונקציה לבין מצביע לפונקציה אינו שונה באמת
זה של אובייקט ומצביע לאובייקט.
ב-C הדוגמה הקנונית של מצביע לפונקציה היא a
מצביע-לפונקציה-מחזיר-שלם (PFI). עבור PFI שלוקח פרמטר int אחד, זה
ניתן להכריז כך:
int (*pfi)(int arg) = 0;
מה שאתה מקבל מזה הוא משתנה בשם פשוט pfi שמאוחל לערך 0.
אם אתה רוצה לאתחל את המצביע הזה למשהו בעל משמעות, אתה חייב שיהיה לך א
פונקציה עם חתימה תואמת. במקרה הזה:
int MyFunction (int arg) {}
אם יש לך יעד זה, אתה יכול לאתחל את המשתנה כך שיצביע על הפונקציה שלך כמו:
pfi = MyFunction;
לאחר מכן תוכל להתקשר ל-MyFunction בעקיפין באמצעות הצורה המרמזת יותר של השיחה:
int result = (*pfi) (1234);
זה מרמז מכיוון שנראה שאתה מפנה את מצביע הפונקציה רק
כמו שהיית מפנה לכל מצביע. עם זאת, בדרך כלל, אנשים מנצלים את
עובדה שהמהדר יודע מה קורה ופשוט ישתמש בצורה קצרה יותר:
int result = pfi (1234);
שימו לב שמצביע הפונקציה מציית לסמנטיקה של ערכים, כך שתוכלו להעביר אותו כמו כל אחד
ערך אחר. בדרך כלל, כאשר אתה משתמש בממשק אסינכרוני תעבור ישות כלשהי
כמו זה לפונקציה שתבצע פעולה ו שיחה בחזרה כדי ליידע אותך על כך
הושלם. זה מתקשר בחזרה על ידי ביצוע העקיפה וביצוע הפונקציה שסופקה.
ב-C++ יש לך את המורכבות הנוספת של אובייקטים. האנלוגיה עם ה-PFI לעיל פירושה אתה
יש מצביע לפונקציית איבר שמחזירה int (PMI) במקום המצביע אל
פונקציה המחזירה int (PFI).
ההכרזה על המשתנה המספק את העקיפה נראית שונה רק במקצת:
int (MyClass::*pmi) (int arg) = 0;
זה מכריז על משתנה בשם PMI בדיוק כפי שהדוגמה הקודמת הכריזה על משתנה בשם
pfi. מכיוון שהרצון יהיה לקרוא למתודה של מופע של מחלקה מסוימת, חייבים
להכריז על השיטה הזו במחלקה:
class MyClass {
פּוּמְבֵּי:
int MyMethod (int arg);
};
בהינתן הצהרת מחלקה זו, לאחר מכן ניתן לאתחל את המשתנה הזה כך:
pmi = &MyClass::MyMethod;
זה מקצה את הכתובת של הקוד המיישם את השיטה למשתנה, משלים
העקיפה. כדי לקרוא למתודה, הקוד צריך א זֶה מַצבִּיעַ. זה, בתורו,
פירושו שחייב להיות אובייקט של MyClass להתייחס אליו. דוגמה פשטנית לכך היא צודקת
קריאה למתודה באופן עקיף (חשבו על פונקציה וירטואלית):
int (MyClass::*pmi) (int arg) = 0; // הכריז על PMI
pmi = &MyClass::MyMethod; // הצבע על קוד היישום
MyClass myClass; // צריך מופע של המחלקה
(myClass.*pmi) (1234); // קרא למתודה עם אובייקט ptr
בדיוק כמו בדוגמה C, אתה יכול להשתמש בזה בקריאה אסינכרונית למודול אחר
אשר יהיה שיחה בחזרה באמצעות שיטה ומצביע אובייקט. ההרחבה הפשוטה
אפשר לשקול זה להעביר מצביע לאובייקט ולמשתנה PMI. המודול
פשוט היה עושה:
(*objectPtr.*pmi) (1234);
כדי לבצע את ההתקשרות חזרה על האובייקט הרצוי.
אפשר לשאול בזמן הזה, מה מה היא נקודה? המודול שנקרא יצטרך להבין
הסוג הקונקרטי של האובייקט המתקשר כדי לבצע את ההתקשרות חזרה. למה לא
פשוט קבל את זה, העבר את מצביע האובייקט שהוקלד נכון ועשה חפץ->שִׁיטָה(1234) in
הקוד במקום ההתקשרות חזרה? זו בדיוק הבעיה שתוארה לעיל. מה זה
need היא דרך לנתק את הפונקציה הקוראת מהמחלקה הנקראת לחלוטין. זֶה
הדרישה הובילה לפיתוח של פונקטור.
פונקטור הוא פועל יוצא של משהו שהומצא בשנות ה-1960 שנקרא סגירה. זה
בעצם רק שיחת פונקציה ארוזה, אולי עם מדינה כלשהי.
לפונקטור יש שני חלקים, חלק ספציפי וחלק גנרי, הקשורים דרך ירושה.
הקוד המתקשר (הקוד שמבצע את ההתקשרות חזרה) יבצע עומס גנרי
מפעיל () של פונקציה גנרית כדי לגרום להתקשרות חוזרת להיקרא. הקוד שנקרא (ה
קוד שרוצה להיקרא בחזרה) יצטרך לספק יישום מיוחד של
מה היא מפעיל () שמבצע את העבודה הספציפית לכיתה שגרמה לצימוד ההדוק
בעיה למעלה.
עם הפונקטור הספציפי ועומס יתר על המידה מפעיל () נוצר, הקוד שנקרא אז
נותן את הקוד המיוחד למודול שיבצע את ההתקשרות חזרה (הקריאה
קוד).
הקוד המתקשר ייקח פונקטור גנרי כפרמטר, כך שנעשה cast מרומז
בקריאת הפונקציה להמרת הפונקטור הספציפי לפונקטור גנרי. זה אומר
שהמודול הקורא רק צריך להבין את סוג הפונקטור הגנרי. זה מנותק
מקוד החיוג לחלוטין.
המידע שצריך כדי ליצור מנגנון ספציפי הוא מצביע האובייקט וה-
כתובת מצביע לשיטה.
המהות של מה שצריך לקרות היא שהמערכת מצהירה על חלק גנרי של
פונקציה:
תבנית
Class Functor
{
פּוּמְבֵּי:
virtual int operator() (T arg) = 0;
};
המתקשר מגדיר חלק מסוים בפונקטור שבאמת נמצא שם כדי ליישם
המסוים מפעיל () שיטה:
תבנית
class SpecificFunctor: Functor ציבורי
{
פּוּמְבֵּי:
SpecificFunctor(T* p, int (T::*_pmi)(ARG arg))
{
m_p = p;
m_pmi = _pmi;
}
virtual int operator() (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
פרטי you
int (T::*m_pmi)(ARG arg);
T* m_p;
};
הנה דוגמה לשימוש:
כיתה א '
{
פּוּמְבֵּי:
A (int a0) : a (a0) {}
int Hello (int b0)
{
std::cout << "שלום מאת A, a = " << a << " b0 = " << b0 << std::endl;
}
int a;
};
main main ()
{
A a(10);
SpecificFunctor sf(&a, &A::Hello);
sf(5);
}
הערה:
הקוד הקודם אינו קוד ns-3 אמיתי. זהו קוד דוגמה פשטני המשמש רק
הדגימו את המושגים המעורבים וכדי לעזור לכם להבין את המערכת יותר. אל
צפו למצוא את הקוד הזה בכל מקום בעץ ns-3.
שימו לב שיש שני משתנים המוגדרים במחלקה למעלה. המשתנה m_p הוא ה-
object pointer ו-m_pmi הוא המשתנה המכיל את הכתובת של הפונקציה to
לבצע.
שימו לב שמתי מפעיל () נקרא, זה בתורו קורא לשיטה שסופקה עם
מצביע אובייקט באמצעות תחביר C++ PMI.
כדי להשתמש בזה, לאחר מכן אפשר להכריז על קוד דגם כלשהו שלוקח פונקטור גנרי בתור א
פָּרָמֶטֶר:
void LibraryFunction (פונקטור Functor);
הקוד שידבר עם המודל יבנה פונקטור ספציפי ויעביר אותו אליו
LibraryFunction:
MyClass myClass;
SpecificFunctor funktor (&myclass, MyClass::MyMethod);
מתי LibraryFunction נעשה, הוא מבצע את ההתקשרות חזרה באמצעות ה מפעיל () על הגנרי
functionor זה הועבר, ובמקרה הספציפי הזה, מספק את ארגומנט המספרים השלמים:
לבטל את
LibraryFunction (פונקטור Functor)
{
// בצע את פונקציית הספרייה
פונקציה(1234);
}
שים לב ש LibraryFunction מנותקת לחלוטין מהסוג הספציפי של הלקוח.
החיבור נעשה באמצעות פולימורפיזם Functor.
ה-Callback API ב ns-3 מיישמת התקשרות מוכוונת עצמים באמצעות מנגנון הפונקטור.
ממשק API זה, המבוסס על תבניות C++, בטוח לסוג; כלומר, הוא מבצע סטטי
בדיקות סוג כדי לאכוף תאימות חתימה נכונה בין מתקשרים למתקשרים. זה
לכן בטוח יותר לשימוש מאשר מצביעי פונקציות מסורתיים, אך ייתכן שהתחביר
נראה מרשים בהתחלה. חלק זה נועד להדריך אותך דרך מערכת ה-Callback
כדי שיהיה לך נוח להשתמש בו ns-3.
שימוש מה היא Callback API
ה-Callback API הוא מינימלי למדי, ומספק שני שירותים בלבד:
1. הצהרת סוג callback: דרך להכריז על סוג של callback עם חתימה נתונה,
ו,
2. מופע התקשרות חוזרת: דרך להפעיל התקשרות חוזרת שנוצרת באמצעות תבנית
שיכול להעביר כל שיחות לשיטת חבר בכיתה C++ או לפונקציית C++ אחרת.
ניתן לראות זאת בצורה הטובה ביותר באמצעות הליכה דרך דוגמה, המבוססת על samples/main-callback.cc.
שימוש מה היא Callback API עם סטטי פונקציות
שקול פונקציה:
כפול סטטי
CbOne (כפול a, כפול b)
{
std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
להחזיר א;
}
שקול גם את קטע התוכנית הראשי הבא:
int main (int argc, char * argv [])
{
// סוג החזרה: כפול
// סוג ארג ראשון: כפול
// סוג ארג שני: כפול
התקשר חזרה אחד;
}
זוהי דוגמה להתקשרות חוזרת בסגנון C -- כזו שאינה כוללת או צריכה א זֶה
מַצבִּיעַ. תבנית הפונקציה Callback הוא בעצם ההכרזה על המשתנה
המכיל את המצביע לפונקציה. בדוגמה למעלה, הצגנו מצביע במפורש
לפונקציה שהחזירה מספר שלם ולקחה מספר שלם בודד כפרמטר, The
Callback פונקציית template היא גרסה גנרית של זה -- היא משמשת להכרזה על הסוג
של התקשרות חוזרת.
הערה:
קוראים שאינם מכירים תבניות C++ עשויים להתייעץ
http://www.cplusplus.com/doc/tutorial/templates/.
השמיים Callback תבנית דורשת ארגומנט חובה אחד (סוג ההחזרה של הפונקציה to
יוקצו להתקשרות חוזרת זו) ועד חמישה ארגומנטים אופציונליים, שכל אחד מהם מציין את
סוג הארגומנטים (אם לפונקציית ההתקשרות הספציפית שלך יש יותר מחמישה ארגומנטים,
אז ניתן לטפל בכך על ידי הרחבת יישום ה-callback).
אז בדוגמה שלמעלה, יש לנו התקשרות חוזרת מוכרזת בשם "אחד" שתעשה בסופו של דבר
להחזיק מצביע פונקציה. החתימה של הפונקציה שהיא תחזיק חייבת לחזור
כפול וחייב לתמוך בשני טיעונים כפולים. אם מנסים לעבור פונקציה של מי
החתימה אינה תואמת את ההתקשרות המוצהרת, תתרחש שגיאת קומפילציה. כמו כן, אם
מנסים להקצות להתקשרות חוזרת לא תואם, ההידור יצליח אבל א
זמן ריצה NS_FATAL_ERROR יוגדל. התוכנית לדוגמה
src/core/examples/main-callback.cc מדגים את שני מקרי השגיאה הללו בסוף
מה היא עיקרי () תכנית.
כעת, עלינו לקשור יחד את מופע ההתקשרות חזרה ופונקציית היעד בפועל
(CbOne). שימו לב לעיל של-CbOne יש את אותם סוגי חתימת פונקציות כמו ה-callback--
זה חשוב. אנחנו יכולים להעביר כל פונקציה כזו שהוקלדה כהלכה להתקשרות חוזרת זו.
בואו נסתכל על זה יותר מקרוב:
סטטי כפול CbOne (כפול a, כפול b) {}
^ ^ ^
| | |
| | |
התקשר חזרה אחד;
אתה יכול לאגד פונקציה להתקשרות חוזרת רק אם יש לה את החתימה התואמת. הראשון
ארגומנט התבנית הוא סוג ההחזרה, והארגומנטים הנוספים של התבנית הם הטיפוסים
של הארגומנטים של חתימת הפונקציה.
כעת, בואו נקשר את ההתקשרות חזרה "אחד" לפונקציה התואמת את החתימה שלה:
// בנה מופע callback המצביע על פונקציית cbOne
one = MakeCallback (&CbOne);
הקריאה הזו ל בצע התקשרות חזרה הוא, בעצם, יצירת אחד מהפונקציות המתמחות
מוזכר לעיל. המשתנה המוצהר באמצעות ה- Callback פונקציית התבנית עומדת
לשחק את התפקיד של הפונקטור הגנרי. המשימה אחד = בצע התקשרות חזרה (&CbOne) is
צוות השחקנים שממיר את המפקח המתמחה המוכר למועד לעובד גנרי
ידוע למתקשר.
לאחר מכן, בהמשך התוכנית, אם יש צורך בהתקשרות חוזרת, ניתן להשתמש בה באופן הבא:
NS_ASSERT (!one.IsNull ());
// הפעל את פונקציית cbOne באמצעות מופע התקשרות חוזרת
retOne כפול;
retOne = one (10.0, 20.0);
המחאה עבור IsNull() מבטיח שההתקשרות חזרה אינה ריק -- שיש פונקציה
להתקשר מאחורי התקשרות חוזרת זו. לאחר מכן, אחד() מבצע את הגנרי מפעיל () שזה באמת
עמוס יתר על המידה ביישום ספציפי של מפעיל () ומחזיר את אותה תוצאה כאילו
CbOne() התקשרו ישירות.
שימוש מה היא Callback API עם חבר פונקציות
בדרך כלל, לא תקרא לפונקציות סטטיות אלא לפונקציות של חבר ציבורי של
חפץ. במקרה זה, יש צורך בארגומנט נוסף לפונקציה MakeCallback, to
אמור למערכת באיזה אובייקט יש להפעיל את הפונקציה. שקול את הדוגמה הזו,
גם מ-main-callback.cc:
class MyCb {
פּוּמְבֵּי:
int CbTwo (כפול a) {
std::cout << "invoke cbTwo a=" << a << std::endl;
return -5;
}
};
int main ()
{
...
// סוג החזרה: int
// סוג ארג ראשון: כפול
התקשר חזרה שתיים;
MyCb cb;
// בנה מופע התקשרות חוזר המצביע על MyCb::cbTwo
two = MakeCallback (&MyCb::CbTwo, &cb);
...
}
כאן, אנו מעבירים מצביע אובייקט נוסף ל- בצע התקשרות חוזרת<> פוּנקצִיָה. נזכר מ
קטע הרקע שמעל זה מַפעִיל() ישתמש בתחביר המצביע לחבר כאשר הוא
מבצע על אובייקט:
virtual int operator() (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
ולכן היינו צריכים לספק את שני המשתנים (m_p ו m_pmi) כאשר עשינו את הספציפי
פונקטור. השורה:
two = MakeCallback (&MyCb::CbTwo, &cb);
עושה בדיוק את זה. במקרה זה, מתי שתיים () הוא מופעל:
int result = two (1.0);
יגרום להתקשרות אל CbTwo פונקציית חבר (שיטה) על האובייקט שעליו מצביע
&cb.
בִּניָן Null התקשרות חוזרת
ייתכן שההתקשרויות חוזרות יהיו ריק; לכן זה עשוי להיות חכם לבדוק לפני השימוש בהם.
יש מבנה מיוחד להתקשרות חוזרת אפסית, שעדיף על מעבר פשוט
"0" בתור ארגומנט; זה MakeNullCallback<> לִבנוֹת:
two = MakeNullCallback ();
NS_ASSERT (two.IsNull ());
הפעלת callback null זה בדיוק כמו הפעלת מצביע פונקציה null: הוא יקרוס בשעה
זמן ריצה.
כָּרוּך התקשרות חוזרת
הרחבה שימושית מאוד לקונספט הפונקטור היא זו של התקשרות חוזרת. בעבר זה
הוזכר שסגירות היו במקור קריאות פונקציה שנארזו למועד מאוחר יותר
ביצוע. שימו לב שבכל תיאורי ה-Callback למעלה, אין דרך לעשות זאת
ארוז את כל הפרמטרים לשימוש מאוחר יותר -- כאשר ה Callback נקרא דרך מפעיל ().
כל הפרמטרים מסופקים על ידי פונקציית ההתקשרות.
מה אם רוצים לאפשר לפונקציית הלקוח (זו שמספקת את ההתקשרות חזרה).
לספק חלק מהפרמטרים? אלכסנדרסקו קורא לתהליך של מתן אפשרות ללקוח
ציין אחד מהפרמטרים "כריכה". אחד הפרמטרים של מפעיל () כבר
כבול (תוקן) על ידי הלקוח.
חלק מקוד המעקב שלנו ל-pcap מספק דוגמה נחמדה לכך. יש פונקציה ש
צריך לקרוא בכל פעם שמתקבלת חבילה. פונקציה זו קוראת לאובייקט כך
למעשה כותב את החבילה לדיסק בפורמט הקובץ pcap. החתימה של אחד מאלה
הפונקציות יהיו:
ריק סטטי DefaultSink (Ptr קובץ, Ptr p);
מילת המפתח הסטטית פירושה שזו פונקציה סטטית שאינה צריכה א זֶה מצביע, אז
זה ישתמש בהתקשרות חוזרת בסגנון C. אנחנו לא רוצים שקוד החיוג צריך לדעת עליו
הכל מלבד החבילה. מה שאנחנו רוצים בקוד החיוג הוא רק שיחה שנראית כך:
m_promiscSnifferTrace (m_currentPkt);
מה שאנחנו רוצים לעשות זה לעשות לאגד מה היא Ptr פילה להתקשרות חוזרת הספציפית
יישום כאשר הוא נוצר ולארגן את מפעיל () של ההתקשרות חזרה אל
לספק את הפרמטר הזה בחינם.
אנו מספקים את MakeBoundCallback פונקציית תבנית למטרה זו. זה לוקח אותו דבר
פרמטרים כמו ה בצע התקשרות חזרה פונקציית תבנית אלא גם לוקחת את הפרמטרים להיות
כָּרוּך. במקרה של הדוגמה למעלה:
MakeBoundCallback (&DefaultSink, קובץ);
תיצור יישום callback ספציפי שיודע להוסיף את הגבול הנוסף
טיעונים. מבחינה קונספטואלית, הוא מרחיב את הפונקטור הספציפי שתואר לעיל עם אחד או יותר
טיעונים קשורים:
תבנית
class SpecificFunctor: Functor ציבורי
{
פּוּמְבֵּי:
SpecificFunctor(T* p, int (T::*_pmi)(ARG arg), BOUND_ARG boundArg)
{
m_p = p;
m_pmi = pmi;
m_boundArg = boundArg;
}
virtual int operator() (ARG arg)
{
(*m_p.*m_pmi)(m_boundArg, arg);
}
פרטי you
void (T::*m_pmi)(ARG arg);
T* m_p;
BOUND_ARG m_boundArg;
};
אתה יכול לראות שכאשר הפונקטור הספציפי נוצר, הארגומנט המחובר נשמר ב-
עובד / אובייקט התקשרות חוזר עצמו. כאשר מפעיל () מופעל עם הסינגל
פרמטר, כמו ב:
m_promiscSnifferTrace (m_currentPkt);
יישום מפעיל () מוסיף את הפרמטר המחובר לקריאת הפונקציה בפועל:
(*m_p.*m_pmi)(m_boundArg, arg);
אפשר לאגד גם שניים או שלושה טיעונים. נגיד שיש לנו פונקציה עם
חֲתִימָה:
ריק סטטי NotifyEvent (Ptr a, Ptr b, MyEventType e);
אפשר ליצור התקשרות חוזרת מחייבת את שני הארגומנטים הראשונים כמו:
MakeBoundCallback (&NotifyEvent, a1, b1);
בהנחה a1 ו b1 הם אובייקטים מסוג A ו B בהתאמה. באופן דומה לשלושה
ארגומנטים אחד תהיה פונקציה עם חתימה:
ריק סטטי NotifyEvent (Ptr a, Ptr b, MyEventType e);
מחייב שלושה ארגומנטים נעשה עם:
MakeBoundCallback (&NotifyEvent, a1, b1, c1);
שוב בהנחה a1, b1 ו c1 הם אובייקטים מסוג A, B ו C בהתאמה.
סוג זה של כריכה יכול לשמש להחלפת מידע בין אובייקטים בסימולציה;
באופן ספציפי, ניתן להשתמש בהתקשרות חוזרת קשורה כהתקשרות חוזרת, אשר יתוארו ב
החלק הבא.
עקבות התקשרות חוזרת
מציין מיקום סעיף קטן
Callback מקומות in ns-3
היכן נעשה שימוש תכוף בהתקשרות חוזרת ns-3? הנה כמה מהאפשרויות הגלויות יותר
משתמשים טיפוסיים:
· Socket API
· API של Layer-2/Layer-3
· תת-מערכת מעקב
· API בין IP ותתי מערכות ניתוב
יישום פרטים
קטעי הקוד שלמעלה הם פשטניים ונועדו רק להמחיש את המנגנון
עצמו. קוד ה-Callback בפועל הוא די מסובך ועתיר תבניות מאוד
הבנה מעמיקה של הקוד אינה נדרשת. אם מעוניינים, משתמשים מומחים עשויים למצוא את
הבא שימושי.
הקוד נכתב במקור על סמך הטכניקות המתוארות ב
http://www.codeproject.com/cpp/TTLFunction.asp. לאחר מכן הוא נכתב מחדש להמשך
הארכיטקטורה המתוארת ב מודרני C + + עיצוב, כללי תכנות ו עיצוב דפוסי
יישם, אלכסנדרסקו, פרק 5, כללית פקטורים.
קוד זה משתמש ב:
· פרמטרי תבנית ברירת מחדל חוסכים למשתמשים את הצורך לציין פרמטרים ריקים מתי
מספר הפרמטרים קטן מהמספר המרבי הנתמך
· ניב pimpl: מעמד ה-Callback מועבר לפי ערך ומאציל את עיקרו של
העבודה אל מצביע הפצעונים שלה.
· ניתן להשתמש בשני יישומי pimpl הנובעים מ-CallbackImpl FunctorCallbackImpl
עם כל סוג פונקטור בעוד MemPtrCallbackImpl יכול לשמש עם מצביעים לחבר
פונקציות.
· יישום רשימת הפניות ליישום סמנטיקה של ערכי Callback.
קוד זה יוצא במיוחד מהיישום של אלכסנדרסקו בכך שהוא לא
השתמש ברשימות סוגים כדי לציין ולהעביר את סוגי הארגומנטים להתקשרות חוזרת. כמובן,
הוא גם אינו משתמש בסמנטיקה של השמדת העתקה ומסתמך על רשימת הפניות במקום
autoPtr כדי להחזיק את המצביע.
חפץ מודל
ns-3 היא בעצם מערכת אובייקט C++. ניתן להכריז על אובייקטים וליצור מופע כמו
רגיל, לפי כללי C++. ns-3 מוסיף גם כמה תכונות לאובייקטי C++ מסורתיים, כמו
המתואר להלן, כדי לספק פונקציונליות ותכונות גבוהות יותר. פרק מדריך זה הוא
נועד להכיר לקורא את ns-3 מודל אובייקט.
סעיף זה מתאר את עיצוב המחלקה C++ עבור ns-3 חפצים. בקיצור, כמה עיצובים
דפוסים בשימוש כוללים עיצוב קלאסי מונחה עצמים (ממשקים פולימורפיים ו
יישומים), הפרדת ממשק ויישום, הציבור הלא וירטואלי
דפוס עיצוב ממשק, מתקן צבירת אובייקטים וספירת הפניות עבור
ניהול זיכרון. מי שמכיר את דגמי הרכיבים כמו COM או Bonobo יעשה זאת
לזהות אלמנטים של העיצוב ב ns-3 מודל צבירת אובייקטים, אם כי ns-3
העיצוב אינו בהתאם לאף אחד מהם.
מונחה עצמים התנהגות
אובייקטי C++, באופן כללי, מספקים יכולות מונחה עצמים נפוצות (הפשטה,
אנקפסולציה, תורשה ופולימורפיזם) שהם חלק ממונחי עצמים קלאסיים
עיצוב. ns-3 חפצים עושים שימוש במאפיינים אלה; לדוגמה:
כתובת הכיתה
{
פּוּמְבֵּי:
כתובת ();
כתובת (סוג uint8_t, const uint8_t *buffer, uint8_t len);
כתובת (const כתובת וכתובת);
כתובת &operator = (const כתובת &address);
...
פרטי you
uint8_t m_type;
uint8_t m_len;
...
};
חפץ בסיס כיתות
ישנן שלוש מחלקות בסיס מיוחדות בשימוש ns-3. כיתות היורשים מבסיס אלו
מחלקות יכולות ליצור אובייקטים עם מאפיינים מיוחדים. מחלקות הבסיס הללו הן:
· מעמד חפץ
· מעמד ObjectBase
· מעמד SimpleRefCount
זה לא נדרש כך ns-3 אובייקטים יורשים מהמחלקה הזו, אבל אלו שכן מקבלים
מאפיינים מיוחדים. שיעורים הנובעים מהשיעור חפץ קבל את המאפיינים הבאים.
· ה ns-3 סוג ומערכת תכונות (ראה תכונות)
· מערכת צבירת אובייקטים
· מערכת ספירת התייחסות מצביע חכמה (מחלקה Ptr)
שיעורים הנובעים מהשיעור ObjectBase קבל את שני הנכסים הראשונים לעיל, אך לא
לקבל עצות חכמות. שיעורים הנובעים מהשיעור SimpleRefCount: קבל רק את
מערכת ספירת הפניות של מצביע חכמה.
בפועל, שיעור חפץ הוא הגרסה של השלושה לעיל כי ns-3 המפתח ירצה
המפגש הנפוץ ביותר.
זכרון ניהול ו בכיתה Ptr
ניהול זיכרון בתוכנת C++ הוא תהליך מורכב, ולעתים קרובות נעשה בצורה שגויה או
באופן לא עקבי. הסתפקנו בתכנון ספירת התייחסות המתואר כדלקמן.
כל האובייקטים המשתמשים בספירת הפניות שומרים על ספירת התייחסות פנימית כדי לקבוע
כאשר אובייקט יכול למחוק את עצמו בבטחה. בכל פעם שמתקבל מצביע ל-an
ממשק, ספירת ההתייחסות של האובייקט מוגברת על ידי קריאה Ref(). זה
חובת המשתמש במצביע במפורש Unref() המצביע בסיום. מתי
ספירת ההתייחסות יורדת לאפס, האובייקט נמחק.
· כאשר קוד הלקוח משיג מצביע מהאובייקט עצמו באמצעות יצירת אובייקט,
או באמצעות GetObject, זה לא חייב להגדיל את ספירת ההפניות.
· כאשר קוד הלקוח משיג מצביע ממקור אחר (למשל, העתקת מצביע) הוא חייב
שיחה Ref() כדי להגדיל את ספירת הפניות.
· כל המשתמשים במצביע האובייקט חייבים להתקשר Unref() כדי לשחרר את ההפניה.
הנטל להתקשר Unref() מוקל מעט מהשימוש בספירת ההתייחסות
מחלקת מצביע חכם המתוארת להלן.
משתמשים המשתמשים ב-API ברמה נמוכה שרוצים להקצות באופן מפורש אובייקטים שאינם נספרים הפניות
on the heap, באמצעות אופרטור new, אחראים למחיקת אובייקטים כאלה.
התייחסות ספירה חכם מצביע (Ptr)
יִעוּד Ref() ו Unref() כל הזמן יהיה מסורבל, אז ns-3 מספק חכם
מחלקה מצביע Ptr דומה Boost::intrusive_ptr. כיתת מצביע חכם זו מניחה זאת
הסוג הבסיסי מספק זוג של אסמכתא ו Unref שיטות שצפויות
הגדל והקטין את הספירה מחדש הפנימית של מופע האובייקט.
יישום זה מאפשר לך לתפעל את המצביע החכם כאילו הוא רגיל
מצביע: אתה יכול להשוות אותו עם אפס, להשוות אותו מול מצביעים אחרים, להקצות אפס ל
זה וכו'.
אפשר לחלץ את המצביע הגולמי מהמצביע החכם הזה עם ה- GetPointer()
ו PeekPointer() שיטות.
אם ברצונך לאחסן אובייקט חדש במצביע חכם, אנו ממליצים לך להשתמש ב-
פונקציות תבנית CreateObject כדי ליצור את האובייקט ולאחסן אותו במצביע חכם
למנוע דליפות זיכרון. פונקציות אלו הן באמת פונקציות נוחות קטנות והמטרה שלהן
זה רק כדי לחסוך לך מעט הקלדה.
CreateObject ו צור
אובייקטים ב-C++ עשויים להיווצר באופן סטטי, דינמי או אוטומטי. זה נכון
ל ns-3 גם, אבל לאובייקטים מסוימים במערכת יש כמה מסגרות נוספות זמינות.
באופן ספציפי, אובייקטים שנספרו הפניות מוקצים בדרך כלל באמצעות תבנית Create or
שיטת CreateObject, כדלקמן.
עבור אובייקטים שמקורם בכיתה חפץ:
Ptr device = CreateObject ();
נא לא ליצור אובייקטים כאלה באמצעות מפעיל חדש; ליצור אותם באמצעות CreateObject ()
במקום.
עבור אובייקטים שמקורם בכיתה SimpleRefCount, או אובייקטים אחרים התומכים בשימוש ב-
מחלקה חכמה מצביע, פונקציית עוזר בתבנית זמינה ומומלצת לשימוש:
Ptr b = צור ();
זהו פשוט מעטפת סביב מפעיל חדש שמטפל בצורה נכונה בספירת הפניות
מערכת.
לסיכום, השתמש לִיצוֹר אם B אינו אובייקט אלא רק משתמש בספירת הפניות (למשל
מנה), ולהשתמש CreateObject אם ב' נובע מ ns3::Object.
צבירה
השמיים ns-3 מערכת צבירת אובייקטים מונעת במידה רבה על ידי הכרה כי א
מקרה שימוש נפוץ עבור ns-2 היה השימוש בירושה ופולימורפיזם כדי להרחיב
מודלים של פרוטוקולים. לדוגמה, גרסאות מיוחדות של TCP כגון RenoTcpAgent נגזרות
מהמחלקה TcpAgent (ולעקוף פונקציות מהמחלקה).
עם זאת, שתי בעיות שעלו ב- ns-2 המודל מושפל ו"בסיס חלש
מחלקה." הורדה מתייחסת להליך של שימוש במצביע מחלקה בסיסית לאובייקט ו
שאילתה אותו בזמן ריצה כדי לגלות מידע על סוג, המשמש להטלה מפורשת של המצביע
למצביע משנה כך שניתן יהיה להשתמש ב-API של תת המעמד. מעמד בסיס חלש מתייחס ל
בעיות המתעוררות כאשר לא ניתן לעשות שימוש חוזר יעיל במחלקה (להפיק ממנה) בגלל שהיא
חסר פונקציונליות הכרחית, מה שמוביל את המפתח לשינוי מחלקת הבסיס ו
גורם להתרבות של קריאות API של מעמד הבסיס, שחלקן עשויות להיות לא מבחינה סמנטית
נכון עבור כל תת המחלקות.
ns-3 משתמש בגרסה של תבנית עיצוב ממשק השאילתה כדי למנוע בעיות אלו.
עיצוב זה מבוסס על אלמנטים של רכיב חפץ מספר סימוכין ו גנומה הבונובו למרות
תאימות מלאה ברמה בינארית של רכיבים הניתנים להחלפה אינה נתמכת ויש לנו
ניסה לפשט את התחביר וההשפעה על מפתחי מודלים.
דוגמאות
צבירה דוגמה
צומת הוא דוגמה טובה לשימוש בצבירה ב ns-3. שימו לב שאין נגזרים
כיתות של צמתים ב ns-3 כמו שיעור InternetNode. במקום זאת, רכיבים (פרוטוקולים) הם
מצטבר לצומת. בואו נסתכל כיצד מתווספים כמה פרוטוקולי Ipv4 לצומת.:
חלל סטטי
AddIpv4Stack(Ptr צוֹמֶת)
{
Ptr ipv4 = CreateObject ();
ipv4->SetNode (צומת);
node->AggregateObject (ipv4);
Ptr ipv4Impl = CreateObject ();
ipv4Impl->SetIpv4 (ipv4);
node->AggregateObject (ipv4Impl);
}
שים לב שהפרוטוקולים של Ipv4 נוצרים באמצעות CreateObject (). לאחר מכן, הם מצטברים
לצומת. באופן זה, אין צורך לערוך את מחלקת הבסיס של צומת כדי לאפשר למשתמשים
עם מצביע Node בכיתה בסיס לגישה לממשק Ipv4; משתמשים עשויים לבקש מהצומת א
מצביע לממשק ה-IPv4 שלו בזמן ריצה. כיצד המשתמש שואל את הצומת מתואר ב-
סעיף קטן הבא.
שים לב שזו שגיאת תכנות לצבור יותר מאובייקט אחד מאותו סוג
an ns3::Object. אז, למשל, צבירה היא לא אפשרות לאחסון כל
שקעים פעילים של צומת.
Getobject דוגמה
GetObject היא דרך בטוחה לסוג להשיג הורדה בטוחה ולאפשר ממשקים להיות
נמצא על חפץ.
שקול מצביע צומת m_node שמצביע על אובייקט Node שיש לו מימוש של
IPv4 הצטבר אליו בעבר. קוד הלקוח רוצה להגדיר מסלול ברירת מחדל. ל
לעשות זאת, עליו לגשת לאובייקט בתוך הצומת שיש לו ממשק להעברת ה-IP
תְצוּרָה. הוא מבצע את הפעולות הבאות:
Ptr ipv4 = m_node->GetObject ();
אם לצומת למעשה אין אובייקט Ipv4 מצטבר אליו, אז השיטה תהיה
החזר null. לכן, מומלץ לבדוק את ערך ההחזרה מפונקציה כזו
שִׂיחָה. אם זה מצליח, המשתמש יכול כעת להשתמש ב-Ptr לאובייקט Ipv4 שהיה בעבר
מצטבר לצומת.
דוגמה נוספת לאופן שבו אפשר להשתמש בצבירה היא הוספת מודלים אופציונליים לאובייקטים. ל
לדוגמה, לאובייקט Node קיים יכול להיות אובייקט "מודל אנרגטי" שנצבר אליו ב-
זמן ריצה (ללא שינוי והידור מחדש של מחלקת הצומת). דגם קיים (כגון א
התקן רשת אלחוטית) יכול לאחר מכן "GetObject" עבור מודל האנרגיה ולפעול כראוי
אם הממשק היה מובנה באובייקט הצומת הבסיסי או צבר אליו
זה בזמן ריצה. עם זאת, צמתים אחרים אינם צריכים לדעת דבר על מודלים של אנרגיה.
אנו מקווים שמצב תכנות זה ידרוש הרבה פחות צורך של מפתחים לשנות
שיעורי הבסיס.
חפץ מפעלים
מקרה שימוש נפוץ הוא ליצור הרבה אובייקטים בעלי תצורה דומה. אפשר שוב ושוב
שיחה CreateObject () אבל יש גם דפוס עיצוב מפעל בשימוש ב ns-3 מערכת.
נעשה בו שימוש רב בממשק ה-API של "העוזר".
כיתה ObjectFactory יכול לשמש ליצירת אובייקטים וכדי להגדיר את התכונות
אותם חפצים:
void SetTypeId (TypeId tid);
void Set (std::string name, const AttributeValue &value);
Ptr צור (בטל) const;
השיטה הראשונה מאפשרת להשתמש ב- ns-3 מערכת TypeId לציון סוג האובייקטים
נוצר. השני מאפשר להגדיר תכונות על האובייקטים שייווצרו, ואת
שלישית מאפשרת ליצור את האובייקטים בעצמם.
לדוגמה:
מפעל ObjectFactory;
// הפוך את המפעל הזה ליצור אובייקטים מסוג FriisPropagationLossModel
factory.SetTypeId ("ns3::FriisPropagationLossModel")
// הפוך את אובייקט המפעל הזה לשנות ערך ברירת מחדל של תכונה, עבור
// יצרו אובייקטים לאחר מכן
factory.Set ("SystemLoss", DoubleValue (2.0));
// צור אובייקט אחד כזה
Ptr object = factory.Create ();
factory.Set ("SystemLoss", DoubleValue (3.0));
// צור אובייקט אחר עם SystemLoss שונה
Ptr object = factory.Create ();
הורדה
שאלה שעלתה מספר פעמים היא, "אם יש לי מצביע מחלקה בסיס (Ptr) ל-
אובייקט ואני רוצה את מצביע המחלקה הנגזרת, האם עלי להוריד (דרך C++ cast דינמי) אל
לקבל את המצביע הנגזר, או שעלי להשתמש במערכת צבירת האובייקטים כדי GetObject<> ()
למצוא Ptr לממשק למשתת ה-API?"
התשובה לכך היא שבמצבים רבים, שתי הטכניקות יעבדו. ns-3 מספק
פונקציה תבניתית להפיכת התחביר של הליהוק דינמי של אובייקט להרבה יותר משתמש
יְדִידוּתִי:
תבנית
Ptr
DynamicCast (Ptr const&p)
{
חזרה Ptr (דינאמי_קאסט (PeekPointer (p)));
}
DynamicCast פועל כאשר למתכנת יש מצביע מסוג בסיס והוא בודק מול א
מצביע משנה. GetObject פועל כאשר מחפשים אובייקטים שונים מצטברים, אבל גם
עובד עם מחלקות משנה, באותו אופן כמו DynamicCast. אם לא בטוח, המתכנת צריך
השתמש ב-GetObject, מכיוון שהוא עובד בכל המקרים. אם המתכנת יודע את היררכיית המעמד של
האובייקט הנדון, ישיר יותר פשוט להשתמש ב- DynamicCast.
תְצוּרָה ו תכונות
In ns-3 סימולציות, ישנם שני היבטים עיקריים לתצורה:
· טופולוגיית הסימולציה וכיצד אובייקטים מחוברים.
· הערכים המשמשים את המודלים המופקים בטופולוגיה.
פרק זה מתמקד בפריט השני שלמעלה: כיצד הערכים הרבים נמצאים בשימוש ns-3 יש לו
מאורגן, מתועד וניתן לשינוי על ידי ns-3 משתמשים. ה ns-3 מערכת התכונות היא גם ה
הבסיס לאופן איסוף עקבות וסטטיסטיקה בסימולטור.
במהלך פרק זה נדון בדרכים השונות להגדיר או לשנות את הערכים
בשימוש על ידי ns-3 מודל אובייקטים. בסדר גובר של הספציפיות, אלה הם:
┌─────────────────────────────────┬─────────────── ───────────────────┐
│שיטה │ היקף │
├─────────────────────────────────┼─────────────── ───────────────────┤
│ ערכי תכונה מוגדרים │ משפיעים על כל המופעים של │
│כאשר תכונות מוגדרות במחלקה │. │
│GetTypeId (). │ │
└─────────────────────────────────┴─────────────── ───────────────────┘
│CommandLine │ להשפיע על כל המופעים העתידיים. │
│Config::SetDefault() │ │
│ConfigStore │ │
├─────────────────────────────────┼─────────────── ───────────────────┤
│ObjectFactory │ משפיע על כל המופעים שנוצרו │
│ │ עם המפעל. │
├─────────────────────────────────┼─────────────── ───────────────────┤
│XHelperSetAttribute () │ משפיע על כל המופעים שנוצרו על ידי │
│ │ העוזר. │
├─────────────────────────────────┼─────────────── ───────────────────┤
│MyClass::SetX () │ משנה את המופע הספציפי הזה. │
│Object::SetAttribute () │ בדרך כלל זוהי הצורה היחידה │
│Config::Set() │ שניתן לתזמן לשינוי │
│ │ מופע לאחר הסימולציה │
│ │ פועל. │
└─────────────────────────────────┴─────────────── ───────────────────┘
ב"ספציפיות" אנו מתכוונים לכך ששיטות בשורות מאוחרות יותר בטבלה עוקפות את הערכים שנקבעו
על ידי, ובדרך כלל משפיעים על פחות מופעים מאשר, שיטות קודמות.
לפני שנעמיק בפרטים של מערכת ערכי התכונות, זה יעזור לסקור כמה
מאפיינים בסיסיים של הכיתה חפץ.
חפץ סקירה כללית
ns-3 היא ביסודה מערכת מבוססת אובייקטים C++. בכך אנו מתכוונים למחלקות C++ חדשות
ניתן להצהיר, להגדיר ולסווג (סוגים) כרגיל.
רב ns-3 חפצים יורשים מה חפץ מעמד בסיס. לחפצים אלה יש עוד כמה
מאפיינים שאנו מנצלים לארגון המערכת ושיפור ניהול הזיכרון
מהחפצים שלנו:
· מערכת "Metadata" המקשרת את שם המחלקה להרבה מטא-מידע על
חפץ, כולל:
· מחלקת הבסיס של תת המחלקה,
· מערך הבנאים הנגישים בתת-הכיתה,
· קבוצת ה"תכונות" של תת המחלקה,
· האם ניתן להגדיר כל תכונה, או שהיא לקריאה בלבד,
· טווח הערכים המותר עבור כל תכונה.
· יישום מצביע חכם ספירת הפניות, לניהול זיכרון.
ns-3 אובייקטים המשתמשים במערכת התכונות נובעים מכל אחד מהם חפץ or ObjectBase. רוב
ns-3 אובייקטים מהם נדון נובעים חפץ, אבל כמה שהם מחוץ לחכמים
מסגרת ניהול זיכרון מצביע נובעת ObjectBase.
הבה נסקור כמה מאפיינים של אובייקטים אלה.
חכם מצביעים
כפי שהוצג ב- ns-3 הדרכה, ns-3 אובייקטים הם זיכרון המנוהל על ידי א הפניה
ספירה חכם מצביע הפעלה, מעמד Ptr.
מצביעים חכמים נמצאים בשימוש נרחב ב ns-3 ממשקי API, כדי להימנע מהעברת הפניות אליהם
אובייקטים שהוקצו בערימה שעלולים לגרום לדליפות זיכרון. לרוב השימוש הבסיסי (תחביר), טפל
מצביע חכם כמו מצביע רגיל:
Ptr nd = ...;
nd->CallSomeFunction ();
// וכו.
אז איך משיגים מצביע חכם לאובייקט, כמו בשורה הראשונה של הדוגמה הזו?
CreateObject
כפי שדנו למעלה ב-Memory-management-and-class-Ptr, ב-API ברמה הנמוכה ביותר, אובייקטים
של סוג חפץ אינם מופעלים באמצעות מפעיל חדש כרגיל אבל במקום זאת על ידי תבנית
פונקציה הנקראת CreateObject ().
דרך טיפוסית ליצור אובייקט כזה היא כדלקמן:
Ptr nd = CreateObject ();
אתה יכול לחשוב על זה כשווה ערך מבחינה תפקודית ל:
WifiNetDevice* nd = WifiNetDevice חדש ();
חפצים הנובעים מ חפץ יש להקצות על הערימה באמצעות CreateObject (). אלה
נובע מ ObjectBase, כמו ns-3 פונקציות עוזר וכותרות מנות וקדימונים,
ניתן להקצות על הערימה.
בכמה סקריפטים, ייתכן שלא תראה הרבה CreateObject () שיחות בקוד; זה
כי יש כמה אובייקטים עוזרים למעשה שעושים את CreateObject () שיחות
בשבילך.
TypeId
ns-3 שיעורים הנובעים משיעור חפץ יכול לכלול מחלקת מטא נתונים בשם TypeId זֶה
מתעד מטא-מידע על המחלקה, לשימוש בצבירה ורכיב האובייקטים
מערכות מנהל:
· מחרוזת ייחודית המזהה את הכיתה.
· מחלקת הבסיס של תת המחלקה, בתוך מערכת המטא נתונים.
· מערך הבנאים הנגישים בתת המחלקה.
· רשימה של נכסים נגישים לציבור ("תכונות") של המחלקה.
חפץ <br> סיכום
אם נחבר את כל המושגים האלה ביחד, בואו נסתכל על דוגמה ספציפית: מחלקה צומת.
קובץ הכותרות הציבורי node.h יש הצהרה הכוללת סטטי GetTypeId ()
שיחת פונקציה:
class Node: public Object
{
פּוּמְבֵּי:
סטטי TypeId GetTypeId (ריק);
...
זה מוגדר ב- node.cc הגש כדלקמן:
TypeId
Node::GetTypeId (ריק)
{
static TypeId tid = TypeId ("ns3::Node")
.SetParent ()
.AddConstructor ()
.AddAttribute ("DeviceList",
"רשימת המכשירים המשויכים לצומת זה.",
ObjectVectorValue (),
MakeObjectVectorAccessor (&Node::m_devices),
MakeObjectVectorChecker ())
.AddAttribute ("ApplicationList",
"רשימת היישומים המשויכים לצומת זה.",
ObjectVectorValue (),
MakeObjectVectorAccessor (&Node::m_applications),
MakeObjectVectorChecker ())
.AddAttribute ("זיהוי",
"מזהה (מספר שלם ייחודי) של צומת זה.",
TypeId::ATTR_GET, // אפשר לקבל אותו בלבד.
UintegerValue (0),
MakeUintegerAccessor (&Node::m_id),
MakeUintegerChecker ())
;
לחזור tid;
}
שקול את TypeId של ns-3 חפץ מחלקה כצורה מורחבת של סוג זמן ריצה
מידע (RTTI). שפת C++ כוללת סוג פשוט של RTTI על מנת לתמוך
- ו טיפוסי אופרטורים.
השמיים SetParent () call בהגדרה לעיל משמש בשילוב עם שלנו
מנגנוני צבירת אובייקטים כדי לאפשר השלכה בטוחה למעלה ולמטה בעצי ירושה
בְּמַהֲלָך Getobject (). זה גם מאפשר לתת-מחלקות לרשת את התכונות של ההורה שלהן
מעמד.
השמיים AddConstructor () call משמש בשילוב עם מפעל האובייקטים המופשטים שלנו
מנגנונים שיאפשרו לנו לבנות אובייקטי C++ מבלי לאלץ משתמש לדעת את
מחלקה בטון של החפץ שהיא בונה.
שלושת השיחות אל AddAttribute () לשייך מחרוזת נתונה לערך מוקלד חזק ב
הכיתה. שים לב שעליך לספק מחרוזת עזרה שעשויה להיות מוצגת, למשל,
באמצעות מעבדי שורת הפקודה. כל אחד תכונה קשור למנגנוני גישה
משתנה האיבר הבסיסי באובייקט (לדוגמה, MakeUintegerAccessor () אומר
הגנרית תכונה קוד כיצד להגיע למזהה הצומת למעלה). יש גם "בודק"
שיטות המשמשות לאימות ערכים מול מגבלות טווח, כגון מקסימום ו
ערכי מינימום מותרים.
כאשר משתמשים רוצים ליצור Nodes, הם בדרך כלל יתקשרו בצורה כלשהי של CreateObject (),:
Ptr n = CreateObject ();
או באופן מופשט יותר, באמצעות מפעל אובייקטים, אתה יכול ליצור א צומת חפץ בלי אפילו
הכרת סוג C++ הבטון:
מפעל ObjectFactory;
const std::string typeId = "ns3::Node'';
factory.SetTypeId (typeId);
Ptr node = factory.Create ();
שתי השיטות הללו מביאות לכך שתכונות אתחול מלא זמינות ב-
וכתוצאה מכך חפץ מקרים.
בהמשך נדון כיצד תכונות (ערכים המשויכים למשתני חבר או פונקציות של
הכיתה) נכללים באמור לעיל TypeId.
תכונות
המטרה של מערכת התכונות היא לארגן את הגישה של אובייקטי חבר פנימיים של a
סימולציה. מטרה זו נובעת מכיוון שבדרך כלל בסימולציה, המשתמשים יחתכו ו
להדביק/לשנות סקריפטים קיימים של סימולציה, או להשתמש במבני סימולציה ברמה גבוהה יותר,
אך לעתים קרובות יהיה מעוניין ללמוד או להתחקות אחר משתנים פנימיים מסוימים. ל
לדוגמה, השתמש במקרים כגון:
· "I רוצה ל להתחקות מה היא מנות on מה היא אלחוטי ממשק רק on מה היא ראשון גישה נְקוּדָה."
· "I רוצה ל להתחקות מה היא ערך of מה היא TCP גודש חלון (כֹּל זמן it שינויים) on a
מסוים TCP שֶׁקַע."
· "I רוצה a שפך of את כל ערכים זֶה היו מְשׁוּמָשׁ in my סימולציה."
באופן דומה, משתמשים עשויים לרצות גישה עדינה למשתנים פנימיים בסימולציה, או
ייתכן שתרצה לשנות באופן כללי את הערך ההתחלתי המשמש עבור פרמטר מסוים בסך הכל
לאחר מכן נוצרו אובייקטים. לבסוף, משתמשים עשויים לרצות לדעת אילו משתנים ניתנים להגדרה
וניתן לשליפה בתצורת סימולציה. זה לא רק לסימולציה ישירה
אינטראקציה בשורת הפקודה; שקול גם ממשק משתמש גרפי (עתידי) כי
רוצה להיות מסוגל לספק תכונה לפיה משתמש עשוי ללחוץ לחיצה ימנית על צומת על
בד וראה רשימה היררכית ומאורגנת של פרמטרים הניתנים להגדרה ב-
צומת ואובייקטי החברים המרכיבים שלו, וטקסט עזרה וערכי ברירת מחדל עבור כל אחד מהם
פָּרָמֶטֶר.
מגדיר תכונות
אנו מספקים דרך למשתמשים לגשת לערכים עמוק במערכת, ללא צורך בצנתור
אביזרים (מצביעים) דרך המערכת והליכה שרשראות מצביעים כדי להגיע אליהם. קחו בחשבון את א
בכיתה DropTailQueue שיש לו משתנה איבר שהוא מספר שלם ללא סימן m_maxPackets;
משתנה חבר זה שולט בעומק התור.
אם נסתכל על ההצהרה של DropTailQueue, אנו רואים את הדברים הבאים:
class DropTailQueue : תור ציבורי {
פּוּמְבֵּי:
סטטי TypeId GetTypeId (ריק);
...
פרטי you
std::queue > m_packets;
uint32_t m_maxPackets;
};
בואו נשקול דברים שמשתמש עשוי לרצות לעשות עם הערך של m_maxPackets:
· הגדר ערך ברירת מחדל עבור המערכת, כך בכל פעם חדש DropTailQueue נוצר,
חבר זה מאותחל לברירת המחדל הזו.
· הגדר או קבל את הערך בתור שכבר נוצר.
הדברים שלעיל דורשים בדרך כלל לספק לקבוע () ו לקבל () פונקציות, וסוג כלשהו של
ערך ברירת מחדל גלובלי.
ב ns-3 מערכת תכונות, הגדרות ערכים אלו ורישום פונקציות עזר
מועברים ל- TypeId מעמד; לְמָשָׁל.:
NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);
TypeId
DropTailQueue::GetTypeId (בטל)
{
static TypeId tid = TypeId ("ns3::DropTailQueue")
.SetParent ()
.AddConstructor ()
.AddAttribute ("MaxPackets",
"מספר החבילות המרבי שמתקבל על ידי DropTailQueue זה.",
UintegerValue (100),
MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
MakeUintegerChecker ())
;
לחזור tid;
}
השמיים AddAttribute () השיטה מבצעת מספר דברים עבור m_maxPackets ערך:
· קשירת משתנה האיבר (בדרך כלל פרטי). m_maxPackets למחרוזת ציבורית
"MaxPackets".
· מתן ערך ברירת מחדל (100 מנות).
· מתן טקסט עזרה כלשהו המגדיר את משמעות הערך.
· מתן "בודק" (לא בשימוש בדוגמה זו) שניתן להשתמש בו כדי להגדיר גבולות על
טווח ערכים מותר.
נקודת המפתח היא שכעת הערך של המשתנה הזה וערך ברירת המחדל שלו נגישים
במרחב שמות המאפיינים, המבוסס על מחרוזות כגון "MaxPackets" ו TypeId שם
מחרוזות. בסעיף הבא, נספק סקריפט לדוגמה שמראה כיצד משתמשים יכולים
לתמרן את הערכים האלה.
שימו לב שהאתחול של התכונה מסתמך על המאקרו NS_OBJECT_ENSURE_REGISTERED
(DropTailQueue) נקרא; אם תשאיר זאת מחוץ ליישום הכיתה החדש שלך, שלך
תכונות לא יאותחלו כהלכה.
אמנם תיארנו כיצד ליצור תכונות, אך עדיין לא תיארנו כיצד לגשת
ולנהל את הערכים הללו. למשל, אין globals.h קובץ header שבו אלה נמצאים
מְאוּחסָן; תכונות מאוחסנות עם הכיתות שלהן. שאלות שעולות באופן טבעי הן איך
האם משתמשים לומדים בקלות על כל התכונות של המודלים שלהם, וכיצד משתמש עושה זאת
לגשת לתכונות אלו, או לתעד את הערכים שלהן כחלק מהרשומה שלהן
סימולציה?
תיעוד מפורט של התכונות בפועל שהוגדרו עבור סוג, ורשימה גלובלית של
כל התכונות המוגדרות זמינות בתיעוד ה-API. לשאר הדברים
במסמך אנחנו הולכים להדגים את הדרכים השונות לקבל ולהגדיר תכונה
ערכים.
הגדרת בְּרִירַת מֶחדָל ערכים
Config::SetDefault ו CommandLine
הבה נבחן כיצד סקריפט משתמש עשוי לגשת לערך תכונה ספציפי. אנחנו הולכים ל
להשתמש src/point-to-point/examples/main-attribute-value.cc תסריט להמחשה, עם
חלק מהפרטים נמחקו. ה ראשי הפונקציה מתחילה:
// זוהי דוגמה בסיסית כיצד להשתמש במערכת התכונות כדי
// הגדר וקבל ערך במערכת הבסיסית; כלומר, לא חתום
// מספר שלם של המספר המרבי של מנות בתור
//
int
main (int argc, char *argv[])
{
// כברירת מחדל, לתכונה MaxPackets יש ערך של 100 מנות
// (ניתן לראות ברירת מחדל זו בפונקציה DropTailQueue::GetTypeId)
//
// כאן, הגדרנו את זה ל-80 מנות. נוכל להשתמש באחד משני סוגי ערכים:
// ערך מבוסס מחרוזת או ערך Uinteger
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
// קריאת הפונקציה להלן מיותרת
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));
// אפשר למשתמש לעקוף את כל ברירות המחדל ואת האמור לעיל
// SetDefaults () בזמן ריצה, באמצעות ארגומנטים של שורת הפקודה
// לדוגמה, דרך "--ns3::DropTailQueue::MaxPackets=80"
CommandLine cmd;
// זה מספק דרך נוספת להגדיר את הערך משורת הפקודה:
cmd.AddValue ("maxPackets", "ns3::DropTailQueue::MaxPackets");
cmd.Parse (argc, argv);
הדבר העיקרי שיש לשים לב לעיל הם שתי השיחות המקבילות ל Config::SetDefault
(). כך אנו מגדירים את ערך ברירת המחדל עבור כל המופעים לאחר מכן
DropTailQueueס. אנו מדגים ששני סוגים של ערך שיעורים, א StringValue וכן
UintegerValue class, ניתן להשתמש כדי להקצות את הערך לתכונה שנקראת על ידי
"ns3::DropTailQueue::MaxPackets".
אפשר גם לתפעל תכונות באמצעות ה CommandLine; ראינו כמה דוגמאות
מוקדם במדריך. בפרט, פשוט להוסיף טיעון קצרצר
שם, כגון --maxPackets, עבור תכונה שרלוונטית במיוחד עבור הדגם שלך,
במקרה הזה "ns3::DropTailQueue::MaxPackets". יש לזה את התכונה הנוספת ש-
מחרוזת עזרה עבור התכונה תודפס כחלק מהודעת השימוש עבור הסקריפט.
לפרטים נוספים ראו CommandLine תיעוד API.
כעת, ניצור מספר אובייקטים באמצעות ה-API ברמה נמוכה. התורים החדשים שלנו יהיו
אין m_maxPackets אתחול ל-100 מנות, כפי שהוגדר ב-
DropTailQueue::GetTypeId () פונקציה, אבל ל-80 חבילות, בגלל מה שעשינו איתו למעלה
ערכי ברירת מחדל.:
Ptr n0 = CreateObject ();
Ptr net0 = CreateObject ();
n0->AddDevice (net0);
Ptr q = CreateObject ();
net0->AddQueue(q);
בשלב זה, יצרנו סינגל צומת (n0) וסינגל PointToPointNetDevice
(net0), והוסיפו א DropTailQueue (q) כדי net0.
בונים, עוזרים ו ObjectFactory
ניתן להגדיר ולהביא שילובים שרירותיים של תכונות מהמסייע ומהרמה הנמוכה
ממשקי API; או מהבנאים עצמם:
Ptr p =
CreateObjectWithAttributes
("MinX", DoubleValue (-100.0),
"MinY", DoubleValue (-100.0),
"DeltaX", DoubleValue (5.0),
"DeltaY", DoubleValue (20.0),
"GridWidth", UintegerValue (20),
"LayoutType", StringValue ("RowFirst"));
או מממשקי ה-API של עוזר ברמה גבוהה יותר, כגון:
mobility.SetPositionAllocator
("ns3::GridPositionAllocator",
"MinX", DoubleValue (-100.0),
"MinY", DoubleValue (-100.0),
"DeltaX", DoubleValue (5.0),
"DeltaY", DoubleValue (20.0),
"GridWidth", UintegerValue (20),
"LayoutType", StringValue ("RowFirst"));
אנחנו לא מדגים את זה כאן, אבל אתה יכול גם להגדיר ObjectFactory עם ערכים חדשים
עבור תכונות ספציפיות. מופעים שנוצרו על ידי ה ObjectFactory יהיו כאלה
תכונות שנקבעו במהלך הבנייה. זה דומה מאוד לשימוש באחד מממשקי ה-API של עוזר
לשיעור.
לבדיקה, ישנן מספר דרכים להגדיר ערכים עבור תכונות עבור מופעי מחלקה ל be
נוצר in מה היא עתיד:
· Config::SetDefault ()
· CommandLine::AddValue ()
· CreateObjectWithAttributes<> ()
· ממשקי API עוזרים שונים
אבל מה אם כבר יצרת מופע, ואתה רוצה לשנות את הערך של
תְכוּנָה? בדוגמה זו, כיצד נוכל לתפעל את m_maxPackets הערך של כבר
מיידי DropTailQueue? להלן דרכים שונות לעשות זאת.
משתנה ערכים
SmartPointer
נניח שמצביע חכם (Ptr) למכשיר רשת רלוונטי ביד; בנוכחי
לדוגמה, זה ה net0 מַצבִּיעַ.
דרך אחת לשנות את הערך היא לגשת למצביע לתור הבסיסי ולשנות שלו
תכונה.
ראשית, אנו רואים שאנו יכולים לקבל מצביע ל- (מחלקה הבסיסית) תור באמצעות מה היא
PointToPointNetDevice תכונות, איפה זה נקרא "TxQueue":
PointerValue tmp;
net0->GetAttribute ("TxQueue", tmp);
Ptr txQueue = tmp.GetObject ();
משתמש ב Getobject () פונקציה, נוכל לבצע ירידה בטוחה ל-a DropTailQueue, שם
"MaxPackets" הוא תכונה:
Ptr dtq = txQueue->GetObject ();
NS_ASSERT (dtq != 0);
לאחר מכן, נוכל לקבל את הערך של תכונה בתור זה. הצגנו עטיפה
ערך מחלקות לסוגי הנתונים הבסיסיים, בדומה ל-Java Wrappers סביב סוגים אלה,
מכיוון שמערכת התכונות מאחסנת ערכים מסודרים למחרוזות, ולא סוגים שונים.
כאן, ערך התכונה מוקצה ל-a UintegerValue, וה לקבל () שיטה בנושא
ערך מייצר את (לא עטוף) uint32_t.:
מגבלת UintegerValue;
dtq->GetAttribute ("MaxPackets", מגבלה);
NS_LOG_INFO ("1. dtq limit: " << limit.Get () << " packets");
שימו לב שאין צורך באמת במדויק לעיל; יכולנו לקבל את התכונה
ערך ישירות מ txQueue, שהוא חפץ:
txQueue->GetAttribute ("MaxPackets", מגבלה);
NS_LOG_INFO ("2. txQueue limit: " << limit.Get () << " packets");
כעת, בואו נגדיר אותו לערך אחר (60 מנות):
txQueue->SetAttribute("MaxPackets", UintegerValue (60));
txQueue->GetAttribute ("MaxPackets", מגבלה);
NS_LOG_INFO ("3. מגבלת txQueue השתנתה: " << limit.Get () << " packets");
Config מרחב שמות נתיב
דרך חלופית להגיע לתכונה היא להשתמש במרחב השמות של התצורה. פה,
תכונה זו נמצאת בנתיב ידוע במרחב השמות הזה; גישה זו שימושית אם כזו
אין לו גישה למצביעים הבסיסיים והוא רוצה להגדיר ספציפי
תכונה עם הצהרה אחת.:
Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets",
UintegerValue (25));
txQueue->GetAttribute ("MaxPackets", מגבלה);
NS_LOG_INFO ("4. מגבלת txQueue השתנתה באמצעות מרחב השמות: "
<< limit.Get () << " packets");
לנתיב התצורה יש לעתים קרובות את הצורה של ".../
שם>/ /.../ / " להתייחס למופע ספציפי לפי אינדקס של an
חפץ במיכל. במקרה זה המיכל הראשון הוא הרשימה של כולם צומתs; ה
מיכל שני הוא הרשימה של כולם NetDevices על הנבחר צומת. סוף - סוף, ה
נתיב תצורה מסתיים בדרך כלל ברצף של תכונות חבר, במקרה זה
"MaxPackets" תכונה של "TxQueue" של הנבחרים NetDevice.
יכולנו גם להשתמש בתווים כלליים כדי להגדיר את הערך הזה עבור כל הצמתים וכל מכשירי הרשת
(שבדוגמה הפשוטה הזו יש את אותה השפעה כמו הקודמת Config::Set ()):
Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets",
UintegerValue (15));
txQueue->GetAttribute ("MaxPackets", מגבלה);
NS_LOG_INFO ("5. מגבלת txQueue השתנתה באמצעות מרחב שמות עם תווים כלליים: "
<< limit.Get () << " packets");
חפץ שם שֵׁרוּת
דרך נוספת להגיע לתכונה היא להשתמש במתקן שירות שם האובייקט. ה
שירות שמות אובייקט מאפשר לנו להוסיף פריטים למרחב השמות של התצורה תחת
"/שמות/" נתיב עם מחרוזת שם המוגדרת על ידי המשתמש. גישה זו שימושית אם לא
יש גישה למצביעים הבסיסיים וקשה לקבוע את הנדרש
נתיב מרחב השמות של תצורת בטון.
שמות::Add ("שרת", n0);
שמות::Add ("שרת/eth0", net0);
...
Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));
כאן הוספנו את רכיבי הנתיב "שרת" ו "eth0" תחת "/שמות/" מרחב שמות, אם כן
השתמש בנתיב התצורה שהתקבל כדי להגדיר את התכונה.
ראה שמות אובייקטים לקבלת טיפול מלא יותר של ns-3 מרחב השמות של תצורה.
יישום פרטים
ערך חוגים
הקוראים ישימו לב ל TypeValue מחלקות שהן תת מחלקות של AttributeValue בסיס
מעמד. ניתן לחשוב על אלה כמחלקות ביניים המשמשות להמרה מ-raw
סוגים ל AttributeValues המשמשים את מערכת התכונות. נזכיר כי זה
מסד הנתונים מחזיק אובייקטים מסוגים רבים המסודרים למחרוזות. המרות לסוג זה
יכול להיעשות באמצעות מחלקת ביניים (כגון ערך שלם, או DoubleValue ל
מספרי נקודה צפה) או באמצעות מחרוזות. המרה מרומזת ישירה של סוגים ל
AttributeValue לא ממש פרקטי. אז באמור לעיל, למשתמשים יש בחירה של שימוש
מחרוזות או ערכים:
p->Set ("cwnd", StringValue ("100")); // מגדיר מבוסס מחרוזת
p->Set ("cwnd", IntegerValue (100)); // מגדיר מבוסס מספרים שלמים
המערכת מספקת כמה פקודות מאקרו שעוזרות למשתמשים להכריז ולהגדיר תכונה חדשה
תת מחלקות לסוגים חדשים שהם רוצים להכניס למערכת התכונות:
· ATTRIBUTE_HELPER_HEADER
· ATTRIBUTE_HELPER_CPP
עיין בתיעוד ה-API עבור מבנים אלה למידע נוסף.
אתחול פריטים שנמכרו
מאפיינים במערכת אינם יכולים להיות תלויים במצב של כל תכונה אחרת בזה
מערכת. הסיבה לכך היא שסדר של אתחול התכונה לא צוין, וגם לא
נאכף על ידי המערכת. דוגמה ספציפית לכך ניתן לראות בתצורה אוטומטית
תוכניות כגון ConfigStore. למרות שמודל נתון עשוי לסדר אותו כך שתכונות
מאתחלים בסדר מסוים, יכול לקבוע תצורה אוטומטית אחרת
באופן עצמאי כדי לשנות תכונות בסדר אלפביתי, למשל.
בגלל הסדר הלא ספציפי הזה, לאף תכונה במערכת עשויה להיות תלות כלשהי
על כל תכונה אחרת. כפועל יוצא, אסור שמציבי תכונות ייכשלו בשל המדינה
של תכונה אחרת. אף קובע תכונה אינו רשאי לשנות (להגדיר) כל ערך תכונה אחר בתור א
תוצאה של שינוי ערכו.
זוהי הגבלה חזקה מאוד ויש מקרים שבהם יש להגדיר מאפיינים
באופן עקבי כדי לאפשר פעולה נכונה. לשם כך אנו כן מאפשרים בדיקת עקביות
מתי מה היא תכונה is מְשׁוּמָשׁ (cf. NS_ASSERT_MSG or NS_ABORT_MSG).
באופן כללי, קוד התכונה להקצאת ערכים למשתני חברי הכיתה הבסיסיים
מבוצע לאחר בניית אובייקט. אבל מה אם אתה צריך את הערכים שהוקצו
לפני שגוף הבנאי מבצע, כי אתה צריך אותם בלוגיקה של
בַּנַאִי? יש דרך לעשות זאת, המשמשת למשל בכיתה ConfigStore: להתקשר
ObjectBase::ConstructSelf () באופן הבא:
ConfigStore::ConfigStore ()
{
ObjectBase::ConstructSelf (AttributeConstructionList ());
// המשך עם הקונסטרוקטור.
}
היזהר שהאובייקט וכל המחלקות הנגזרות שלו חייבים ליישם גם את a GetInstanceTypeId
() שיטה. אחרת ה ObjectBase::ConstructSelf () לא יוכל לקרוא את
תכונות.
מוסיף תכונות
השמיים ns-3 מערכת תציב מספר ערכים פנימיים תחת מערכת התכונות, אבל
אין ספק שמשתמשים ירצו להרחיב את זה כדי לאסוף את אלה שפספסנו, או להוסיף את שלהם
שיעורים משלו למערכת.
ישנם שלושה מקרי שימוש טיפוסיים:
· הפיכת חבר נתוני כיתה קיים לנגיש כתכונה, כאשר היא עדיין לא קיימת.
· הפיכת מחלקה חדשה ליכולת לחשוף חלק מחברי הנתונים כמאפיינים על ידי מתן TypeId.
· יצירת א AttributeValue תת-מחלקה למחלקה חדשה כך שניתן לגשת אליה כ-
תְכוּנָה.
קיים חבר מִשְׁתַנֶה
שקול את המשתנה הזה ב Tcpsocket:
uint32_t m_cWnd; // חלון עומס
נניח שמישהו שעובד עם TCP רצה לקבל או להגדיר את הערך של המשתנה הזה
באמצעות מערכת המטא נתונים. אם זה לא סופק כבר על ידי ns-3, המשתמש יכול להצהיר
התוספת הבאה במערכת המטא נתונים של זמן הריצה (ל- GetTypeId() הגדרה עבור
Tcpsocket):
.AddAttribute ("חלון עומס",
"חלון גודש Tcp (בתים)",
UintegerValue (1),
MakeUintegerAccessor (&TcpSocket::m_cWnd),
MakeUintegerChecker ())
כעת, המשתמש עם מצביע ל-a Tcpsocket מופע יכול לבצע פעולות כגון
הגדרה וקבלת הערך, מבלי להוסיף את הפונקציות הללו במפורש.
יתר על כן, ניתן להחיל בקרות גישה, כגון מתן אפשרות לקרוא את הפרמטר ו
לא כתוב, או שניתן להחיל בדיקת גבולות על הערכים המותרים.
חדש כיתה TypeId
כאן, אנו דנים בהשפעה על משתמש שרוצה להוסיף לו מחלקה חדשה ns-3. מה
יש לעשות דברים נוספים כדי לאפשר לו להחזיק תכונות?
בואו נניח שהכיתה החדשה שלנו, שנקראת ns3::MyMobility, הוא סוג של מודל ניידות. ראשון,
הכיתה צריכה לרשת ממחלקת האב שלה, ns3::MobilityModel. ב my-mobility.h
קובץ הכותרת:
מרחב שמות ns3 {
class MyClass: מודל ניידות ציבורי
{
זה מחייב שנכריז על GetTypeId () פוּנקצִיָה. זוהי פונקציה ציבורית בת שורה אחת
הַצהָרָה:
פּוּמְבֵּי:
/ **
* רשום סוג זה.
* \return האובייקט TypeId.
*/
סטטי TypeId GetTypeId (ריק);
כבר הצגנו מה א TypeId ההגדרה תיראה כמו ב- my-mobility.cc
קובץ יישום:
NS_OBJECT_ENSURE_REGISTERED (MyMobility);
TypeId
MyMobility::GetTypeId (בטל)
{
static TypeId tid = TypeId ("ns3::MyMobility")
.SetParent ()
.SetGroupName ("ניידות")
.AddConstructor ()
.AddAttribute ("גבולות",
"גבולות האזור לשייט.",
RectangleValue (מלבן (0.0, 0.0, 100.0, 100.0)),
MakeRectangleAccessor (&MyMobility::m_bounds),
MakeRectangleChecker ())
.AddAttribute ("זמן",
"שנה את הכיוון והמהירות הנוכחיים לאחר תנועה עבור עיכוב זה.",
TimeValue (שניות (1.0)),
MakeTimeAccessor (&MyMobility::m_modeTime),
MakeTimeChecker ())
// וכו' (פרמטרים נוספים).
;
לחזור tid;
}
אם אנחנו לא רוצים לתת מחלקה קיימת, בקובץ header אנחנו פשוט יורשים
החל מ- ns3::Object, ובקובץ האובייקט אנחנו מגדירים את מחלקת האב ל ns3::Object עם
.SetParent ().
טעויות אופייניות כאן כוללות:
· לא מתקשר NS_OBJECT_ENSURE_REGISTERED ()
· לא מתקשר ל סטנדרנט () שיטה, או לקרוא לה עם הסוג הלא נכון.
· לא מתקשר ל AddConstructor () שיטה, או לקרוא לה עם הסוג הלא נכון.
· הצגת טעות דפוס בשם ה TypeId בבנאי שלה.
· אי שימוש בשם הסוג C++ המלא של המחלקה המקיפה C++ כשם של
TypeId. שים לב ש "ns3::" נדרש.
אף אחת מהטעויות הללו לא יכולה להיות מזוהה על ידי ns-3 בסיס קוד, לכן מומלץ למשתמשים לבדוק
בזהירות מספר פעמים שהם עשו את זה נכון.
חדש AttributeValue סוּג
מנקודת המבט של המשתמש שכותב מחלקה חדשה במערכת ורוצה שתהיה
נגיש כתכונה, יש בעיקר עניין של כתיבת ההמרות מ/אל
מחרוזות וערכי תכונות. את רוב זה ניתן להעתיק/להדביק עם קוד בעל מאקרו. ל
למשל, שקול הצהרת כיתה עבור מלבן ב src/mobility/model Directory:
כותרת שלח
/ **
* \brief מלבן דו מימדי
*/
מחלקה מלבן
{
...
כפול xMin;
כפול xMax;
כפול yMin;
כפול yMax;
};
יש להוסיף שיחת מאקרו אחת ושני אופרטורים מתחת להצהרת המחלקה על מנת
הפוך מלבן לערך שמיש את ה- תכונה מערכת:
std::ostream &operator << (std::ostream &os, const מלבן &מלבן);
std::istream &operator >> (std::istream &is, מלבן &מלבן);
ATTRIBUTE_HELPER_HEADER (מלבן);
יישום שלח
בהגדרת המחלקה (. DC קובץ), הקוד נראה כך:
ATTRIBUTE_HELPER_CPP (מלבן);
std::ostream &
אופרטור << (std::ostream &os, const מלבן &מלבן)
{
os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|"
<< rectangle.yMax;
החזר OS;
}
std::istream &
אופרטור >> (std::istream &is, מלבן &מלבן)
{
char c1, c2, c3;
הוא >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3
>> rectangle.yMax;
if (c1 != '|' ||
c2 != '|' ||
c3 != '|')
{
is.setstate (std::ios_base::failbit);
}
החזרה היא;
}
אופרטורי זרמים אלה פשוט ממירים מייצוג מחרוזת של המלבן
("xMin|xMax|yMin|yMax") למלבן הבסיסי. על המעצב לציין את אלה
אופרטורים והייצוג התחבירי של המחרוזת של מופע של המחלקה החדשה.
ConfigStore
ערכים עבור ns-3 ניתן לאחסן תכונות בקובץ טקסט ASCII או XML ולטעון אותם לתוך a
ריצת סימולציה עתידית. תכונה זו ידועה בשם ns-3 ConfigStore. ה ConfigStore is
מסד נתונים מיוחד לערכי תכונות וערכי ברירת מחדל.
למרות שזה מודול מתוחזק בנפרד ב- src/config-store/ מדריך, אנחנו
לתעד אותו כאן בגלל התלות הבלעדית שלו ns-3 מודול ליבה ותכונות.
אנו יכולים לחקור מערכת זו באמצעות דוגמה מ
src/config-store/examples/config-store-save.cc.
ראשית, כל המשתמשים ב- ConfigStore חייב לכלול את ההצהרה הבאה:
#include "ns3/config-store-module.h"
לאחר מכן, תוכנית זו מוסיפה אובייקט לדוגמה ConfigExample כדי להראות כיצד המערכת מורחבת:
class ConfigExample : אובייקט ציבורי
{
פּוּמְבֵּי:
static TypeId GetTypeId (void) {
static TypeId tid = TypeId ("ns3::A")
.SetParent ()
.AddAttribute ("TestInt16", "טקסט עזרה",
ערך שלם (-2),
MakeIntegerAccessor (&A::m_int16),
MakeIntegerChecker ())
;
לחזור tid;
}
int16_t m_int16;
};
NS_OBJECT_ENSURE_REGISTERED (ConfigExample);
לאחר מכן, אנו משתמשים בתת-מערכת Config כדי לעקוף את ברירות המחדל בכמה דרכים:
Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (-5));
Ptr a_obj = CreateObject ();
NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5,
"לא ניתן להגדיר את התכונה של ConfigExample באמצעות Config::SetDefault");
Ptr a2_obj = CreateObject ();
a2_obj->SetAttribute ("TestInt16", IntegerValue (-3));
IntegerValue iv;
a2_obj->GetAttribute ("TestInt16", iv);
NS_ABORT_MSG_UNLESS (iv.Get () == -3,
"לא ניתן להגדיר את התכונה של ConfigExample באמצעות SetAttribute");
ההצהרה הבאה נחוצה כדי לוודא ש(אחד) האובייקטים שנוצרו מושרשים
במרחב השמות של התצורה כמופע אובייקט. זה קורה בדרך כלל כשאתה
צבירת אובייקטים ל- a ns3::צומת or ns3::ערוץ לדוגמא, אבל כאן, מכיוון שאנחנו עובדים
ברמת הליבה, עלינו ליצור אובייקט מרחב שמות שורש חדש:
Config::RegisterRootNamespaceObject (a2_obj);
כתיבה
לאחר מכן, אנו רוצים להוציא את מאגר התצורה. הדוגמאות מראות כיצד לעשות זאת בשניים
פורמטים, XML וטקסט גולמי. בפועל, יש לבצע את השלב הזה ממש לפני ההתקשרות
סימולטור::Run () כדי לשמור את התצורה הסופית לפני הפעלת הסימולציה.
ישנן שלוש תכונות השולטות בהתנהגות ConfigStore: "מצב",
"שם קובץ", ו "פורמט קובץ". המצב (ברירת מחדל "אף אחד") מגדיר אם ns-3 צריך
טען תצורה מקובץ שנשמר בעבר (ציין "מצב=טעינה") או שמור אותו בקובץ
(לפרט "מצב=שמירה"). שם הקובץ (ברירת מחדל "") הוא המקום שבו ConfigStore צריך לקרוא או
לכתוב את הנתונים שלו. תבנית הקובץ (ברירת מחדל "RawText") קובע אם פורמט ConfigStore
הוא טקסט רגיל או Xml ("FileFormat=Xml")
הדוגמה מראה:
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
ConfigStore outputConfig;
outputConfig.ConfigureDefaults ();
outputConfig.ConfigureAttributes ();
// פלט מאגר תצורה לפורמט txt
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.txt"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
ConfigStore outputConfig2;
outputConfig2.ConfigureDefaults ();
outputConfig2.ConfigureAttributes ();
סימולטור::Run ();
סימולטור::הרוס ();
שימו לב למיקום ההצהרות הללו ממש לפני ה סימולטור::Run () הצהרה.
פלט זה רושם את כל הערכים במקום ממש לפני תחילת הסימולציה (כְּלוֹמַר.
לאחר שכל התצורה התבצעה).
לאחר הריצה, אתה יכול לפתוח את output-attributes.txt קובץ וראה:
ברירת מחדל ns3::RealtimeSimulatorImpl::SynchronizationMode "BestEffort"
ברירת המחדל ns3::RealtimeSimulatorImpl::HardLimit "+100000000.0ns"
ברירת מחדל ns3::PcapFileWrapper::CaptureSize "65535"
ברירת מחדל ns3::PacketSocket::RcvBufSize "131072"
ברירת המחדל ns3::ErrorModel::IsEnabled "true"
ברירת המחדל ns3::RateErrorModel::ErrorUnit "EU_BYTE"
ברירת מחדל ns3::RateErrorModel::ErrorRate "0"
ברירת מחדל ns3::RateErrorModel::RanVar "Uniform:0:1"
ברירת מחדל ns3::DropTailQueue::מצב "חבילות"
ברירת מחדל ns3::DropTailQueue::MaxPackets "100"
ברירת מחדל ns3::DropTailQueue::MaxBytes "6553500"
ברירת מחדל ns3::Application::StartTime "+0.0ns"
ברירת מחדל ns3::Application::StopTime "+0.0ns"
ברירת מחדל ns3::ConfigStore::מצב "שמור"
ברירת המחדל ns3::ConfigStore::שם הקובץ "output-attributes.txt"
ברירת מחדל ns3::ConfigStore::FileFormat "RawText"
ברירת מחדל ns3::ConfigExample::TestInt16 "-5"
RngSeed העולמי "1"
RngRun העולמי "1"
Global SimulatorImplementationType "ns3::DefaultSimulatorImpl"
Global SchedulerType "ns3::MapScheduler"
גלובלי ChecksumEnabled "false"
value /$ns3::ConfigExample/TestInt16 "-3"
לעיל, כל ערכי ברירת המחדל עבור תכונות עבור מודול הליבה מוצגים.
לאחר מכן, כל הערכים עבור ns-3 ערכים גלובליים נרשמים. לבסוף, הערך של
מופע של ConfigExample שהושרש במרחב השמות של התצורה מוצג. ב
ממשי ns-3 בתוכנית, יוצגו עוד דגמים, תכונות וברירות מחדל.
גרסת XML קיימת גם ב output-attributes.xml:
ניתן לאחסן את הקובץ הזה עם סקריפט הסימולציה ונתוני הפלט שלך.
קריאה
לאחר מכן, נדון בהגדרת סימולציות באמצעות קובץ תצורת קלט מאוחסן. יש
כמה הבדלים מרכזיים בהשוואה לכתיבת תצורת הסימולציה הסופית.
ראשית, עלינו למקם הצהרות כגון אלה בתחילת התוכנית, לפני
הצהרות תצורת סימולציה נכתבות (כך שהערכים נרשמים לפני שהם
משמש בבניית אובייקטים).
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
ConfigStore inputConfig;
inputConfig.ConfigureDefaults ();
לאחר מכן, שים לב שטעינת נתוני תצורת הקלט מוגבלת לברירת המחדל של תכונה (כְּלוֹמַר.
לא מופע) ערכים, וערכים גלובליים. ערכי מופע של תכונה אינם נתמכים
כי בשלב זה של הסימולציה, לפני בניית אובייקטים, אין
מופעי אובייקט כאלה מסביב. (שים לב, שיפורים עתידיים בחנות התצורה עשויים להשתנות
ההתנהגות הזאת).
שנית, בעוד הפלט של ConfigStore המדינה יפרט את כל מה שנמצא במסד הנתונים, ה
קובץ הקלט צריך להכיל רק את הערכים הספציפיים שיש לעקוף. אז, דרך אחת להשתמש
מחלקה זו עבור תצורת קובץ קלט היא ליצור תצורה ראשונית באמצעות ה
פלט ("להציל") "מצב" שתואר לעיל, חלץ מאותו קובץ תצורה רק את
אלמנטים שרוצים לשנות, ולהעביר את האלמנטים המינימליים הללו לקובץ תצורה חדש
אשר לאחר מכן ניתן לערוך ולטעון בבטחה בריצת סימולציה שלאחר מכן.
כאשר ConfigStore האובייקט הוא מופע, התכונות שלו "שם קובץ", "מצב", ו
"פורמט קובץ" חייב להיות מוגדר, גם כן באמצעות שורת הפקודה או באמצעות הצהרות התוכנית.
קריאה כתיבה דוגמה
כדוגמה מסובכת יותר, נניח שאנו רוצים לקרוא בתצורה של
ברירת מחדל מקובץ קלט בשם input-defaults.xml, ורשום את התוצאה
תכונות לקובץ נפרד בשם output-attributes.xml.:
#include "ns3/config-store-module.h"
...
int main (...)
{
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
ConfigStore inputConfig;
inputConfig.ConfigureDefaults ();
//
// אפשר למשתמש לעקוף את כל ברירות המחדל ואת ה-Bind () לעיל ב
// זמן ריצה, ארגומנטים של שורת פקודה
//
CommandLine cmd;
cmd.Parse (argc, argv);
// טופולוגיית הגדרות
...
// הפעל ממש לפני הכניסה לסימולטור::Run ()
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
ConfigStore outputConfig;
outputConfig.ConfigureAttributes ();
סימולטור::Run ();
}
ConfigStore GUI
יש קצה קצה מבוסס GTK עבור ConfigStore. זה מאפשר למשתמשים להשתמש ב-GUI כדי
לגשת ולשנות משתנים. צילומי מסך של תכונה זו זמינים ב- |ns3|
סקירה כללית הַצָגָה.
כדי להשתמש בתכונה זו, יש להתקין libgtk ו libgtk-dev; אובונטו לדוגמא
פקודת ההתקנה היא:
$ sudo apt-get להתקין libgtk2.0-0 libgtk2.0-dev
כדי לבדוק אם הוא מוגדר או לא, בדוק את הפלט של השלב:
$ ./waf configure --enable-examples --enable-tests
---- סיכום של תכונות NS-3 אופציונליות:
Python Bindings: מופעלים
תמיכה בסריקה של Python API: מופעלת
NS-3 לחץ אינטגרציה: מופעל
GtkConfigStore: לא מופעל (לא נמצאה הספרייה 'gtk+-2.0 >= 2.12')
בדוגמה שלמעלה, זה לא הופעל, ולכן לא ניתן להשתמש בו עד שתהיה גרסה מתאימה
מותקן ו:
$ ./waf configure --enable-examples --enable-tests
$ ./waf
מופעל מחדש.
השימוש כמעט זהה לגרסה שאינה מבוססת GTK, אך אין ConfigStore
תכונות מעורבות:
// הפעל ממש לפני הכניסה לסימולטור::Run ()
תצורת GtkConfigStore;
config.ConfigureDefaults ();
config.ConfigureAttributes ();
כעת, כאשר אתה מפעיל את הסקריפט, GUI אמור להופיע, המאפשר לך לפתוח תפריטים של
תכונות בצמתים/אובייקטים שונים, ולאחר מכן הפעל את ביצוע הסימולציה כאשר אתה
סיימו.
שלנו לעבוד
ישנם כמה שיפורים אפשריים:
· שמור מספר גרסה ייחודי עם תאריך ושעה בתחילת הקובץ.
· שמור rng seed ראשוני איפשהו.
· הפוך כל RandomVariable לסידרה את ה-Seed הראשוני שלו וקרא אותו מחדש מאוחר יותר.
חפץ שמות
מציין מיקום פרק
רישום
השמיים ns-3 ניתן להשתמש במתקן רישום לניטור או ניפוי באגים של התקדמות הסימולציה
תוכניות. ניתן להפעיל פלט רישום על ידי הצהרות תוכנית בקובץ שלך עיקרי () תכנית או
על ידי השימוש ב- NS_LOG משתנה הסביבה.
הצהרות רישום לא מקופלות לבניית אופטימיזציה של ns-3. כדי להשתמש ברישום, אחד
חייב לבנות את ה-bug build (ברירת המחדל) של ns-3.
הפרויקט אינו מבטיח אם פלט הרישום יישאר זהה
זְמַן. משתמשים מוזהרים מפני בניית מסגרות פלט סימולציות על גבי רישום
קוד, שכן הפלט והאופן שבו הפלט מופעל עשויים להשתנות עם הזמן.
סקירה כללית
ns-3 הצהרות רישום משמשות בדרך כלל לרישום אירועים שונים של הפעלת תוכנית, כגון
כמו התרחשות של אירועי סימולציה או שימוש בפונקציה מסוימת.
לדוגמה, קטע הקוד הזה הוא מ Ipv4L3Protocol::IsDestinationAddress():
if (כתובת == iaddr.GetBroadcast ())
{
NS_LOG_LOGIC ("בשבילי (כתובת שידור ממשק)");
return true;
}
אם רישום הופעל עבור Ipv4L3 פרוטוקול רכיב בחומרה של הִגָיוֹן or
למעלה (ראה להלן לגבי חומרת היומן), ההצהרה תודפס; אחרת זה
ידחקו.
מפעיל תְפוּקָה
ישנן שתי דרכים בהן משתמשים בדרך כלל שולטים בפלט יומן. הראשון הוא על ידי הגדרת ה
NS_LOG משתנה הסביבה; לְמָשָׁל:
$ NS_LOG="*" ./waf --הפעל ראשון
יפעיל את ראשון תוכנית הדרכה עם כל פלט רישום. (הפרטים של ה NS_LOG
פורמט יידון להלן.)
ניתן להפוך זאת לפרטני יותר על ידי בחירת רכיבים בודדים:
$ NS_LOG="Ipv4L3Protocol" ./waf --הפעל תחילה
ניתן להתאים יותר את הפלט עם אפשרויות קידומת.
הדרך השנייה לאפשר רישום היא להשתמש בהצהרות מפורשות בתוכנית שלך, כגון ב
מה היא ראשון תוכנית הדרכה:
int
main (int argc, char *argv[])
{
LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
...
(המשמעות של LOG_LEVEL_INFO, וערכים אפשריים אחרים, יידונו להלן.)
NS_LOG תחביר
השמיים NS_LOG משתנה סביבה מכיל רשימה של רכיבי יומן ואפשרויות. עֵץ
הרכיבים מופרדים על ידי תווי `:':
$ NS_LOG=" : ..."
אפשרויות עבור כל רכיב יומן ניתנות כדגלים לאחר כל רכיב יומן:
$ NS_LOG=" = | ...: ..."
אפשרויות שולטות בחומרה וברמה של רכיב זה, והאם אופציונליים
יש לכלול מידע, כגון זמן הסימולציה, צומת הסימולציה, פונקציה
השם, והחומרה הסמלית.
התחבר רכיבי
בדרך כלל, רכיב יומן מתייחס לקוד מקור יחיד . DC קובץ, ומקיף את
הקובץ כולו.
לחלק מהעוזרים יש שיטות מיוחדות לאפשר רישום של כל הרכיבים במודול,
משתרע על יחידות קומפילציה שונות, אך מקובצות באופן הגיוני, כגון ns-3
קוד wifi:
WifiHelper wifiHelper;
wifiHelper.EnableLogComponents ();
השמיים NS_LOG תו כללי '*' של רכיב log יאפשר את כל הרכיבים.
כדי לראות אילו רכיבי יומן מוגדרים, כל אחד מאלה יעבוד:
$ NS_LOG="print-list" ./waf --רוץ ...
$ NS_LOG="foo" # אסימון שאינו תואם לאף רכיב יומן
הטופס הראשון ידפיס את השם והדגלים המאפשרים עבור כל רכיבי היומן שהם
מקושר ב; לנסות את זה עם סימולטור שריטות. הטופס השני מדפיס את כל היומן הרשום
רכיבים, ואז צא עם שגיאה.
חומרה ו רמה אפשרויות
הודעות בודדות שייכות ל"דרגת חומרה" יחידה שנקבעה על ידי המאקרו היוצר את
הוֹדָעָה. בדוגמה למעלה, NS_LOG_LOGIC(..) יוצר את ההודעה ב- LOG_LOGIC
דרגת חומרה.
דרגות החומרה הבאות מוגדרות כ enum קבועים:
┌───────────────┬───────────────────────────────── ─┐
│דרגת חומרה │ משמעות │
├───────────────┼───────────────────────────────── ─┤
│LOG_NONE │ ברירת המחדל, ללא רישום │
├───────────────┼───────────────────────────────── ─┤
│LOG_ERROR │ הודעות שגיאה חמורות בלבד │
├───────────────┼───────────────────────────────── ─┤
│LOG_WARN │ הודעות אזהרה │
├───────────────┼───────────────────────────────── ─┤
│LOG_DEBUG │ לשימוש באיתור באגים │
├───────────────┼───────────────────────────────── ─┤
│LOG_INFO │ מידע │
├───────────────┼───────────────────────────────── ─┤
│LOG_FUNCTION │ מעקב אחר פונקציות │
├───────────────┼───────────────────────────────── ─┤
│LOG_LOGIC │ שליטה במעקב אחר זרימה בתוך │
│ │ פונקציות │
└───────────────┴───────────────────────────────── ─┘
בדרך כלל רוצים לראות הודעות בדרגת חומרה נתונה ו גבוה יותר. זה נעשה על ידי
הגדרת "רמות" רישום כולל:
┌───────────────────┬───────────────────────────── ─────┐
│רמה │ משמעות │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_ERROR │ בלבד LOG_ERROR דרגת חומרה │
│ │ הודעות. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_WARN │ LOG_WARN ומעל. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_DEBUG │ LOG_DEBUG ומעל. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_INFO │ LOG_INFO ומעל. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_FUNCTION │ LOG_FUNCTION ומעל. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_LOGIC │ LOG_LOGIC ומעל. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_ALL │ כל שיעורי החומרה. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_ALL │ מילה נרדפת ל LOG_LEVEL_ALL │
└───────────────────┴───────────────────────────── ─────┘
ניתן לתת את אפשרויות דרגת החומרה והרמה ב- NS_LOG משתנה סביבה על ידי
האסימונים האלה:
┌─────────┬────────────────┐
│כיתה │ רמה │
├─────────┼────────────────┤
│שגיאה │ level_error │
├─────────┼────────────────┤
│להזהיר │ level_warn │
├─────────┼────────────────┤
│באגים │ level_debug │
├─────────┼────────────────┤
│מידע │ level_info │
├─────────┼────────────────┤
│פונקציה │ level_function │
├─────────┼────────────────┤
│הגיון │ level_logic │
├─────────┼────────────────┤
│ │ level_all │
│ │ את כל │
│ │ * │
└─────────┴────────────────┘
שימוש באסימון דרגת חומרה מאפשר הודעות יומן בדרגת חומרה זו בלבד. לדוגמה,
NS_LOG="*=אזהרה" לא יוציא הודעות בחומרה שגיאה. NS_LOG="*=level_debug" יצטרך
פלט הודעות ברמות חומרה באגים ומעל.
ניתן לשלב שיעורי חומרה ורמות עם ה'|' מַפעִיל:
NS_LOG="*=level_warn|לוגיקה" יוציא הודעות ברמות חומרה שגיאה, להזהיר ו הגיון.
השמיים NS_LOG תו כללי '*' של רמת החומרה ו את כל הן מילים נרדפות ל level_all.
עבור רכיבי יומן שהוזכרו רק ב NS_LOG
$ NS_LOG=" :..."
חומרת ברירת המחדל היא LOG_LEVEL_ALL.
קידומת אפשרויות
מספר קידומות יכול לעזור לזהות היכן ומתי מקורה ההודעה, וממה
חוּמרָה.
אפשרויות הקידומת הזמינות (כמו enum קבועים) הם
┌─────────────────┬─────────────────────────────── ───┐
│ סמל קידומת │ משמעות │
├─────────────────┼─────────────────────────────── ───┤
│LOG_PREFIX_FUNC │ קידומת שם המתקשר │
│ │ פונקציה. │
├─────────────────┼─────────────────────────────── ───┤
│LOG_PREFIX_TIME │ קידומת זמן הסימולציה. │
├─────────────────┼─────────────────────────────── ───┤
│LOG_PREFIX_NODE │ קידומת מזהה הצומת. │
├─────────────────┼─────────────────────────────── ───┤
│LOG_PREFIX_LEVEL │ קידומת רמת החומרה. │
├─────────────────┼─────────────────────────────── ───┤
│LOG_PREFIX_ALL │ אפשר את כל הקידומות. │
└─────────────────┴─────────────────────────────── ───┘
אפשרויות הקידומת מתוארות בקצרה להלן.
ניתן לתת את האפשרויות ב- NS_LOG משתנה סביבה לפי האסימונים האלה:
┌─────────────┬───────────┐
│אסימון │ חלופי │
├─────────────┼───────────┤
│prefix_func │ func │
├─────────────┼───────────┤
│prefix_time │ זמן │
└─────────────┴───────────┘
│prefix_node │ צומת │
├─────────────┼───────────┤
│prefix_level │ רָמָה │
├─────────────┼───────────┤
│prefix_all │ את כל │
│ │ * │
└─────────────┴───────────┘
עבור רכיבי יומן שהוזכרו רק ב NS_LOG
$ NS_LOG=" :..."
אפשרויות ברירת המחדל של קידומת הן LOG_PREFIX_ALL.
חומרה קידומת
ניתן לכלול את דרגת החומרה של הודעה עם האפשרויות prefix_level or רָמָה.
לדוגמה, ערך זה של NS_LOG מאפשר רישום עבור כל רכיבי היומן (`*') והכל
שיעורי חומרה (= הכל), ותחיל את ההודעה עם דרגת החומרה (|רמת_תחילית).
$ NS_LOG="*=all|prefix_level" ./waf --הפעל scratch-simulator
סימולטור שריטות
הודעת שגיאה [שגיאה]
הודעת אזהרה [אזהרה]
[DEBUG] הודעת ניפוי באגים
הודעת מידע [INFO]
הודעת פונקציה [FUNCT]
הודעת לוגיקה [LOGIC]
זְמַן קידומת
ניתן לכלול את זמן הסימולציה עם האפשרויות prefix_time or זמן. זה מדפיס את
זמן סימולציה בשניות.
צומת קידומת
ניתן לכלול את מזהה צומת הסימולציה עם האפשרויות prefix_node or צומת.
פונקציה קידומת
ניתן לכלול את שם הפונקציה המתקשרת עם האפשרויות prefix_func or func.
NS_LOG כרטיסי בר
התו הכללי '*' של רכיב היומן יאפשר את כל הרכיבים. כדי להפעיל את כל הרכיבים ב-a
שימוש ברמת חומרה ספציפית *=.
התו הכללי `*' של אפשרות רמת החומרה הוא מילה נרדפת ל את כל. זה חייב להתרחש לפני כל
`|' אפשרויות הפרדת תווים. כדי להפעיל את כל מחלקות החומרה, השתמש =*,
or =*|.
האפשרות '*' או אסימון כללי את כל מאפשר את כל אפשרויות הקידומת, אך חייב להתרחש לאחר a
`|' אופי. כדי לאפשר דרגת חומרה או רמת חומרה ספציפית, וכל הקידומות, השתמש
= |*.
התו הכללי של האפשרות המשולבת ** מאפשר את כל דרגות החומרה ואת כל הקידומות; לדוגמה,
=**.
התו הכללי של אובר *** מאפשר את כל דרגות החומרה ואת כל הקידומות עבור כל רכיבי היומן.
כל אלה שווים:
$ NS_LOG="***" ... $ NS_LOG="*=הכל|*" ... $ NS_LOG="*=*|הכל" ...
$ NS_LOG="*=**" ... $ NS_LOG="*=level_all|*" ... $ NS_LOG="*=*|prefix_all" ...
$ NS_LOG="*=*|*" ...
שימו לב: אפילו הטריוויאלי סימולטור שריטות מייצר מעל 46K קווי פלט עם
NS_LOG="***"!
איך ל להוסיף רישום ל שֶׁלְךָ קוד
הוספת רישום לקוד שלך היא פשוטה מאוד:
1. הפעל את NS_LOG_COMPONENT_DEFINE (...); מאקרו בתוך מרחב שמות Ns3.
צור מזהה מחרוזת ייחודי (בדרך כלל מבוסס על שם הקובץ ו/או המחלקה
מוגדר בתוך הקובץ) ורשום אותו באמצעות קריאת מאקרו כגון:
מרחב שמות ns3 {
NS_LOG_COMPONENT_DEFINE ("פרוטוקול Ipv4L3");
...
זה רושם Ipv4L3 פרוטוקול כרכיב יומן.
(המאקרו נכתב בקפידה כדי לאפשר הכללה בתוך או מחוצה לו
מרחב שמות Ns3, והשימוש ישתנה על פני בסיס הקוד, אבל הכוונה המקורית הייתה לעשות זאת
לרשום את זה בחוץ של מרחב שמות Ns3 בהיקף הגלובלי של הקובץ.)
2. הוסף הצהרות רישום (שיחות מאקרו) לפונקציות ולגופי הפונקציות שלך.
רישום מאקרו
פקודות המאקרו הרישום ורמות החומרה הנלוות הן
┌───────────────┬───────────────────────
│דרגת חומרה │ מאקרו │
├───────────────┼───────────────────────
│LOG_NONE │ (אין צורך) │
├───────────────┼───────────────────────
│LOG_ERROR │ NS_LOG_ERROR (...); │
├───────────────┼───────────────────────
│LOG_WARN │ NS_LOG_WARN (...); │
├───────────────┼───────────────────────
│LOG_DEBUG │ NS_LOG_DEBUG (...); │
├───────────────┼───────────────────────
│LOG_INFO │ NS_LOG_INFO (...); │
├───────────────┼───────────────────────
│LOG_FUNCTION │ NS_LOG_FUNCTION (...); │
├───────────────┼───────────────────────
│LOG_LOGIC │ NS_LOG_LOGIC (...); │
└───────────────┴────────────────────────
פקודות המאקרו מתפקדות כסטרימרי פלט, כך שכל דבר שאתה יכול לשלוח אליו std :: cout, הצטרף
by << מפעילים, מותר:
void MyClass::Check (ערך int, char * פריט)
{
NS_LOG_FUNCTION (פריט << arg << זה);
if (arg > 10)
{
NS_LOG_ERROR ("נתקל בערך לא טוב " << ערך <
" תוך כדי בדיקת " << שם << "!");
}
...
}
שים לב כי NS_LOG_FUNCTION מוסיף אוטומטית ',' (רווח-פסיק) מפריד בין
כל אחד מהטיעונים שלו. זה מפשט רישום של ארגומנטים של פונקציה; רק לשרשר
אותם עם << כמו בדוגמה למעלה.
ללא תנאים רישום
לנוחות, ה NS_LOG_UNCOND (...); מאקרו תמיד ירשום את הארגומנטים שלו, גם אם
רכיב היומן המשויך אינו מופעל בשום חומרה. מאקרו זה אינו משתמש באף אחד
של אפשרויות הקידומת. שים לב שהרישום מופעל רק בבניית ניפוי באגים; המאקרו הזה
לא יפיק פלט בבניית אופטימיזציה.
הנחיות
· התחל כל שיטת מחלקה עם NS_LOG_FUNCTION (זֶה << args...); זה מאפשר קל
מעקב אחר קריאת פונקציה.
· למעט: אל תרשום אופרטורים או בוני העתקה מפורשים, מכיוון שאלו יגרמו
רקורסיה אינסופית והצפת מחסנית.
· עבור מתודות ללא ארגומנטים השתמש באותה צורה: NS_LOG_FUNCTION (זֶה);
· עבור פונקציות סטטיות:
· עם טיעונים שימוש NS_LOG_FUNCTION (...); כרגיל.
· ללא טיעונים שימוש NS_LOG_FUNCTION_NOARGS ();
· להשתמש NS_LOG_ERROR למצבי שגיאה חמורים שכנראה מבטלים את הסימולציה
ביצוע.
· להשתמש NS_LOG_WARN לתנאים חריגים שעשויים להיות ניתנים לתיקון. בבקשה תן כמה רמזים
לגבי מהות הבעיה וכיצד ניתן לתקן אותה.
· NS_LOG_DEBUG משמש בדרך כלל ב- an ad הוק דרך להבין את הביצוע של מודל.
· להשתמש NS_LOG_INFO למידע נוסף על הביצוע, כגון גודל א
מבנה הנתונים בעת הוספה/הסרה ממנו.
· להשתמש NS_LOG_LOGIC להתחקות אחר ענפי לוגיקה חשובים בתוך פונקציה.
· בדוק ששינויי הרישום שלך אינם מפרים את הקוד. הפעל כמה תוכניות לדוגמה עם
כל רכיבי היומן מופעלים (למשל NS_LOG="***").
מעקב
תת-מערכת המעקב היא אחד המנגנונים החשובים ביותר להבנה בהם ns-3,
רוב המקרים, ns-3 למשתמשים יהיה רעיון מבריק לרשת חדשה ומשופרת
תכונה. על מנת לוודא שהרעיון הזה עובד, החוקר יבצע שינויים ב-an
מערכת קיימת ולאחר מכן הרץ ניסויים כדי לראות כיצד התכונה החדשה מתנהגת על ידי איסוף
נתונים סטטיסטיים המתעדים את התנהגות התכונה.
במילים אחרות, כל העניין בהפעלת סימולציה הוא לייצר פלט להמשך
לימוד. ב ns-3, תת המערכת שמאפשרת לחוקר לעשות זאת היא המעקב
תת מערכת.
מעקב מוטיבציה
ישנן דרכים רבות להוציא מידע מתוך תוכנית. הדרך הכי פשוטה היא
פשוט להדפיס ישירות את המידע לפלט הסטנדרטי, כמו ב,
#לִכלוֹל
...
int main ()
{
...
std::cout << "הערך של x הוא " << x << std::endl;
...
}
זה בר ביצוע בסביבות קטנות, אבל ככל שהסימולציות שלך גדלות יותר ויותר
מסובך, אתה בסופו של דבר עם יותר ויותר הדפסים ומשימת הניתוח והביצוע
החישובים על הפלט מתחילים להיות קשים יותר ויותר.
דבר נוסף שיש לקחת בחשבון הוא שבכל פעם שיש צורך במשהו חדש, ליבת התוכנה
יש לערוך ולהציג הדפסה נוספת. אין דרך סטנדרטית לשלוט בכל
של תפוקה זו, כך שכמות התפוקה נוטה לגדול ללא גבולות. בסופו של דבר, ה
רוחב הפס הנדרש לפשוט פלט מידע זה מתחיל להגביל את זמן הריצה
של הסימולציה. קבצי הפלט גדלים לגדלים עצומים וניתוחם הופך ל- a
בעיה.
ns-3 מספק מנגנון פשוט לרישום ומתן שליטה מסוימת על הפלט באמצעות
התחבר רכיבי, אבל רמת השליטה אינה עדינה במיוחד בכלל. הרישום
מודול הוא מכשיר קהה יחסית.
רצוי שיהיה מתקן המאפשר להגיע למערכת הליבה ורק
לקבל את המידע הנדרש מבלי לשנות ולהרכיב מחדש את מערכת הליבה. אֲפִילוּ
עדיף שתהיה מערכת שתודיע למשתמש כאשר פריט עניין השתנה או
קרה אירוע מעניין.
השמיים ns-3 מערכת המעקב נועדה לעבוד לאורך הקווים הללו ומשולבת היטב עם
תת-גזעי ה-Attribute ו-Config המאפשרים תרחישי שימוש פשוטים יחסית.
סקירה כללית
תת-מערכת המעקב מסתמכת במידה רבה על ns-3 מנגנוני התקשרות ותכונה. אתה
צריך לקרוא ולהבין את הסעיפים המתאימים במדריך לפני שתנסה לעשות זאת
להבין את מערכת המעקב.
השמיים ns-3 מערכת מעקב בנויה על המושגים של מקורות מעקב עצמאיים ו
מעקב אחר כיורים; יחד עם מנגנון אחיד לחיבור מקורות לכיורים.
מקורות מעקב הם ישויות שיכולות לאותת על אירועים המתרחשים בסימולציה ולספק
גישה לנתונים בסיסיים מעניינים. לדוגמה, מקור עקבות יכול לציין מתי א
מנה מתקבלת על ידי מכשיר נטו ומספקת גישה לתוכן החבילה עבור
עקבות מתעניינים. מקור עקבות עשוי גם לציין מתי מצב מעניין
שינוי קורה במודל. לדוגמה, חלון הגודש של מודל TCP הוא ראשוני
מועמד למקור עקבות.
מקורות עקבות אינם שימושיים בפני עצמם; הם חייבים להיות מחוברים לחלקי קוד אחרים
שלמעשה עושים משהו מועיל עם המידע שסיפק המקור. ה
ישויות שצורכות מידע עקבות נקראות שקעי עקבות. מקורות עקבות הם
מחוללי אירועים וכיורי עקבות הם צרכנים.
חלוקה מפורשת זו מאפשרת לפזר מסביב למספר רב של מקורות עקבות
המערכת במקומות שלדעתם מחברי המודלים עשויים להיות שימושיים. אלא אם משתמש מחבר א
לשקוע עקבות לאחד מהמקורות הללו, שום דבר אינו פלט. סידור זה מאפשר יחסית
משתמשים לא מתוחכמים לצרף סוגים חדשים של כיורים למקורות מעקב קיימים, ללא
דורש עריכה והידור מחדש של הליבה או הדגמים של הסימולטור.
יכולים להיות אפס או יותר צרכנים של אירועי מעקב שנוצרו על ידי מקור מעקב. אחד יכול
חשבו על מקור עקבות כסוג של קישור מידע מנקודה לריבוי נקודות.
"פרוטוקול התחבורה" עבור קישור רעיוני זה מנקודה לריבוי נקודות הוא ns-3 Callback.
נזכיר ממדור התקשרות חוזרת כי מתקן התקשרות חוזר הוא דרך לאפשר שני מודולים להיכנס
המערכת לתקשר באמצעות קריאות פונקציה ובו זמנית מנתקת את ההתקשרות
פונקציה מהמחלקה הנקראת לחלוטין. זוהי אותה דרישה כפי שתוארה לעיל
עבור מערכת האיתור.
בעצם, מקור עקבות is התקשרות חוזרת אליה ניתן לרשום מספר פונקציות.
כאשר כיור מעקב מביע עניין בקבלת אירועי מעקב, הוא מוסיף התקשרות חזרה ל- a
רשימה של התקשרויות חוזרות המוחזקות על ידי מקור המעקב. כאשר מתרחש אירוע מעניין, העקבות
המקור מפעיל את זה מפעיל () מתן אפס פרמטרים או יותר. זה אומר למקור
עברו על רשימת ההתקשרות האחורית שלו תוך הפעלת כל אחד בתורו. בדרך זו, הפרמטר(ים)
מועברים לשקעי העקבות, שהם רק פונקציות.
השמיים הכי פשוט דוגמה
זה יהיה שימושי ללכת ללכת דוגמה מהירה רק כדי לחזק את מה שאמרנו.:
#include "ns3/object.h"
#include "ns3/uinteger.h"
#include "ns3/traced-value.h""
#include "ns3/trace-source-accessor.h"
#לִכלוֹל
שימוש במרחב שמות ns3;
הדבר הראשון שצריך לעשות הוא לכלול את הקבצים הנדרשים. כאמור, מערכת המעקב
עושה שימוש רב במערכות האובייקט והתכונה. שני הראשונים כוללים להביא את
הצהרות עבור אותן מערכות. הקובץ, traced-value.h מביא את הנדרש
הצהרות למעקב אחר נתונים המצייתים לסמנטיקה ערכית.
באופן כללי, סמנטיקה של ערכים רק אומרת שאתה יכול להעביר את האובייקט מסביב, לא א
כתובת. כדי להשתמש בסמנטיקה של ערך בכלל צריך להיות אובייקט עם an
בנאי העתקות משויך ואופרטור ההקצאות זמין. אנו מרחיבים את הדרישות
לדבר על סט האופרטורים המוגדרים מראש עבור סוגי נתונים רגילים-ישנים (POD).
Operator=, Operator++, Operator--, Operator+, Operator== וכו'.
כל זה אומר שאתה תוכל לעקוב אחר שינויים באובייקט שנעשה באמצעות
אותם מפעילים.:
class MyObject: אובייקט ציבורי
{
פּוּמְבֵּי:
סטטי TypeId GetTypeId (בטל)
{
static TypeId tid = TypeId ("האובייקט שלי")
.SetParent (Object::GetTypeId ())
.AddConstructor ()
.AddTraceSource ("MyInteger",
"ערך מספר שלם למעקב.",
MakeTraceSourceAccessor (&MyObject::m_myInt))
;
לחזור tid;
}
MyObject () {}
TracedValue m_myInt;
};
מכיוון שמערכת המעקב משולבת עם תכונות, ותכונות עובדות עם אובייקטים,
חייב להיות ns-3 חפץ למקור העקבות לחיות בו. שני הקווים החשובים של
הקוד הם .AddTraceSource ו TracedValue הצהרה.
השמיים .AddTraceSource מספק את ה"ווים" המשמשים לחיבור מקור העקבות ל-
עולם חיצוני. ה TracedValue ההצהרה מספקת את התשתית המעמיסה את
מפעילים שהוזכרו לעיל ומניעים את תהליך ההתקשרות חזרה.:
לבטל את
IntTrace (Int oldValue, Int newValue)
{
std::cout << "עקב " << oldValue << " ל" << newValue << std::endl;
}
זו ההגדרה של כיור עקבות. זה מתאים ישירות לפונקציית התקשרות חוזרת.
פונקציה זו תיקרא בכל פעם שאחד מהמפעילים של TracedValue is
יצא לפועל.:
int
main (int argc, char *argv[])
{
Ptr myObject = CreateObject ();
myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));
myObject->m_myInt = 1234;
}
בקטע זה, הדבר הראשון שצריך לעשות הוא ליצור את האובייקט שבו
מקור העקבות חי.
השלב הבא, ה TraceConnectWithoutContext, יוצר את הקשר בין העקבות
מקור וכיור עקבות. שימו לב ל בצע התקשרות חזרה פונקציית תבנית. נזכר מה
קטע התקשרות חוזר שזה יוצר את הפונקטור המתמחה שאחראי לספק את
עמוס מפעיל () משמש כדי "לפטר" את ההתקשרות חזרה. האופרטורים העמוסים (++, -- וכו')
ישתמש בזה מפעיל () למעשה להפעיל את ההתקשרות חזרה. ה TraceConnectWithoutContext,
לוקח פרמטר מחרוזת המספק את שם התכונה שהוקצה למעקב
מָקוֹר. בוא נתעלם מהקטע לגבי ההקשר לעת עתה מכיוון שהוא עדיין לא חשוב.
לבסוף, השורה:
myObject->m_myInt = 1234;
יש לפרש כקריאה ל מפעיל = על משתנה האיבר m_myInt עם
המספר השלם 1234 עבר כפרמטר. מסתבר שהמפעיל הזה מוגדר (על ידי
TracedValue) כדי לבצע התקשרות חוזרת שמחזירה void ולוקחת שני ערכים שלמים כ
פרמטרים -- ערך ישן וערך חדש עבור המספר השלם המדובר. זה בדיוק
חתימת הפונקציה עבור פונקציית ההתקשרות שסיפקנו -- IntTrace.
לסיכום, מקור מעקב הוא, במהותו, משתנה שמכיל רשימה של התקשרויות חוזרות. א
trace sink היא פונקציה המשמשת כיעד להתקשרות חוזרת. התכונה וסוג האובייקט
מערכות מידע משמשות כדי לספק דרך לחבר מקורות עקבות לשקעים. ה
פעולה של "פגיעה" במקור עקבות היא ביצוע של מפעיל על מקור העקבות שיורה
התקשרויות חוזרות. זה גורם להתקשרות חוזרת של העקיבה לרשום עניין במקור
נקרא עם הפרמטרים שסופקו על ידי המקור.
שימוש מה היא Config תת מערכת ל לְחַבֵּר ל עקבות מקורות
השמיים TraceConnectWithoutContext השיחה המוצגת לעיל בדוגמה הפשוטה היא למעשה מאוד
נעשה שימוש לעתים רחוקות במערכת. באופן טיפוסי יותר, ה Config מערכת המשנה משמשת כדי לאפשר בחירה
מקור עקבות במערכת באמצעות מה שנקרא a config נתיב.
לדוגמה, אפשר למצוא משהו שנראה כמו הבא במערכת (נלקח
החל מ- examples/tcp-large-transfer.cc):
void CwndTracer (uint32_t oldval, uint32_t newval) {}
...
Config::ConnectWithoutContext (
"/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
MakeCallback (&CwndTracer));
זה אמור להיראות מאוד מוכר. זה אותו דבר כמו הדוגמה הקודמת, חוץ מזה
פונקציית חבר סטטית של מחלקה Config נקרא במקום שיטה מופעלת חפץ;
ובמקום an תכונה שם, מסופק נתיב.
הדבר הראשון שצריך לעשות הוא לקרוא את השביל אחורה. הקטע האחרון של השביל חייב להיות
an תכונה של חפץ. למעשה, אם היה לך מצביע על חפץ כי יש
"חלון עומס" תכונה שימושי (קוראים לזה האובייקט), אתה יכול לכתוב את זה בדיוק כמו ה
דוגמא קודמת:
void CwndTracer (uint32_t oldval, uint32_t newval) {}
...
theObject->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
מסתבר שהקוד עבור Config::ConnectWithoutContext עושה בדיוק את זה. זֶה
הפונקציה לוקחת נתיב המייצג שרשרת של חפץ מצביעים ועוקב אחריהם עד לזה
מגיע לסוף השביל ומפרש את הקטע האחרון כ-an תכונה על האחרון
לְהִתְנַגֵד. בואו נעבור על מה שקורה.
התו "/" המוביל בנתיב מתייחס למה שנקרא מרחב שמות. אחד מ
מרחבי שמות מוגדרים מראש במערכת התצורה הם "NodeList" שהיא רשימה של כל ה-
צמתים בסימולציה. פריטים ברשימה מתייחסים לפי מדדים לתוך הרשימה, אז
"/NodeList/0" מתייחס לצומת האפס ברשימת הצמתים שנוצרה על ידי הסימולציה.
הצומת הזה הוא למעשה א Ptr וכך גם תת-מחלקה של an ns3::Object.
כפי שתואר בסעיף מודל אובייקט, ns-3 תומך במודל צבירת אובייקטים. ה
קטע הנתיב הבא מתחיל עם התו "$" שמציין א Getobject שיחה צריכה להיות
גרם לחפש את הסוג שאחריו. כאשר צומת מאותחל על ידי an
InternetStackHelper מספר ממשקים מצטברים לצומת. אחד מאלה הוא ה
פרוטוקול TCP רמה ארבע. סוג זמן הריצה של אובייקט פרוטוקול זה הוא ns3::TcpL4Protocol''.
מתי מה היא `` GetObject מבוצע, הוא מחזיר מצביע לאובייקט מסוג זה.
השמיים פרוטוקול TcpL4 class מגדיר תכונה בשם "SocketList" שהיא רשימה של
שקעים. כל שקע הוא למעשה an ns3::Object עם משלה תכונות. הפריטים ב
רשימת השקעים מתייחסת לפי אינדקס בדיוק כמו ב-NodeList, אז "SocketList/0"
מתייחס לשקע האפס ברשימת השקעים בצומת האפס ב-NodeList --
הצומת הראשון שנבנה בסימולציה.
שקע זה, שסוגו מתברר כ- ns3::TcpSocketImpl מגדיר תכונה
שנקרא "CongestionWindow" שהוא א TracedValue. ה
Config::ConnectWithoutContext עכשיו עושה א,:
object->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
באמצעות מצביע האובייקט מ-"SocketList/0" שיוצר את החיבור בין המעקב
מקור שהוגדר בשקע להתקשרות חזרה -- CwndTracer.
עכשיו, בכל פעם שנעשה שינוי ב- TracedValue המייצג את העומס
חלון בשקע TCP, ההתקשרות חזרה הרשומה תתבצע והפונקציה
CwndTracer ייקרא הדפסת הערכים הישנים והחדשים של עומס ה-TCP
חלון.
שימוש מה היא מעקב API
ישנן שלוש רמות של אינטראקציה עם מערכת המעקב:
· משתמש מתחיל יכול לשלוט בקלות אילו אובייקטים משתתפים במעקב;
· משתמשים בינוניים יכולים להרחיב את מערכת המעקב כדי לשנות את פורמט הפלט שנוצר
או להשתמש במקורות עקבות קיימים בדרכים שונות, מבלי לשנות את הליבה של
מַדמֶה;
· משתמשים מתקדמים יכולים לשנות את ליבת הסימולטור כדי להוסיף מקורות מעקב וכיורים חדשים.
שימוש עקבות עוזרים
השמיים ns-3 עוזרי מעקב מספקים סביבה עשירה להגדרת תצורה ובחירה אחרת
להתחקות אחר אירועים ולכתוב אותם לקבצים. בסעיפים הקודמים, בעיקר "בניין
טופולוגיות," ראינו כמה סוגים של שיטות עוזר המעקב המיועדות לשימוש
בתוך עוזרים אחרים (מכשיר).
אולי תזכרו שראיתם כמה מהווריאציות האלה:
pointToPoint.EnablePcapAll ("שני");
pointToPoint.EnablePcap ("שני", p2pNodes.Get (0)->GetId (), 0);
csma.EnablePcap ("שלישי", csmaDevices.Get (0), true);
pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));
מה שאולי לא מובן מאליו, הוא שיש מודל עקבי לכל
שיטות הקשורות לעקבות שנמצאו במערכת. כעת ניקח מעט זמן ונסתכל
ב"תמונה הגדולה".
יש כרגע שני מקרי שימוש עיקריים של עוזרי האיתור ב ns-3: עוזרי מכשירים
ועוזרי פרוטוקול. עוזרי התקנים מסתכלים על הבעיה של ציון אילו עקבות צריכים
להיות מופעל באמצעות צומת, צמד מכשירים. לדוגמה, ייתכן שתרצה לציין את ה-pcap
יש לאפשר מעקב במכשיר מסוים בצומת ספציפי. זה נובע מה
ns-3 דגם קונספטואלי של המכשיר, וגם הדגמים הרעיוניים של המכשירים השונים
עוזרים. בעקבות זאת, הקבצים שנוצרו עוקבים אחר א
- - מוסכמות שמות.
עוזרי פרוטוקול בוחנים את הבעיה של ציון דרך אילו עקבות יש לאפשר
זוג פרוטוקול וממשק. זה נובע מה ns-3 מודל קונספטואלי מחסנית פרוטוקול,
וגם המודלים הרעיוניים של עוזרי מחסנית אינטרנט. באופן טבעי, קבצי המעקב
צריך לעקוב אחר א - - מוסכמות שמות.
לכן עוזרי העקבות נופלים באופן טבעי לטקסונומיה דו-ממדית. יש
דקויות שמונעות מכל ארבעת המעמדות להתנהג בצורה זהה, אבל אנחנו כן שואפים לכך
לגרום לכולם לעבוד בצורה דומה ככל האפשר; ובמידת האפשר יש אנלוגים עבור
כל השיטות בכל המחלקות.
┌────────────────┬──────┬───────
│ │ pcap │ ascii │
├────────────────┼──────┼───────
│מסייע התקן │ │ │
├────────────────┼──────┼───────
│מסייע פרוטוקול │ │ │
└────────────────┴──────┴───────
אנו משתמשים בגישה שנקראת א מיקסין כדי להוסיף פונקציונליות מעקב לשיעורי העוזרים שלנו. א
מיקסין היא מחלקה המספקת פונקציונליות למחלקה שהורשתה על ידי תת מחלקה.
ירושה ממיקסין לא נחשבת לצורת התמחות אבל היא באמת דרך לעשות זאת
איסוף פונקציונליות.
הבה נסתכל במהירות על כל ארבעת המקרים הללו ובהתאמה שלהם מיקסינס.
פפ מעקב מכשיר עוזרים
המטרה של עוזרים אלה היא להקל על הוספת מתקן עקבות pcap עקבי ל- an
ns-3 התקן. אנחנו רוצים שכל הטעמים השונים של ה-pcap tracing יפעלו אותו דבר
כל המכשירים, כך שהשיטות של עוזרים אלה עוברות בירושה לעוזרי מכשירים. תסתכל
at src/network/helper/trace-helper.h אם אתה רוצה לעקוב אחר הדיון תוך כדי התבוננות
קוד אמיתי.
הכיתה PcapHelperForDevice הוא מיקסין מספק את הפונקציונליות ברמה גבוהה לשימוש
מעקב pcap ב-an ns-3 התקן. כל מכשיר חייב ליישם שיטה וירטואלית אחת
בירושה מכיתה זו.:
ריק וירטואלי EnablePcapInternal (std::string prefix, Ptr nd, bool promiscuous) = 0;
החתימה של שיטה זו משקפת את ההשקפה הממוקדת במכשיר של המצב במצב זה
רָמָה. כל השיטות הציבוריות בירושה מהכיתה PcapUserHelperForDevice לצמצם ל
קוראים לשיטת היישום התלויה במכשיר הבודד הזו. לדוגמה, הרמה הנמוכה ביותר
שיטת pcap,:
void EnablePcap (std::string prefix, Ptr nd, bool promiscuous = false, bool explicitFilename = false);
יקרא ליישום המכשיר של EnablePcapInternal ישירות. כל שאר ה-pcap הציבוריים
שיטות מעקב מבוססות על יישום זה כדי לספק רמת משתמש נוספת
פונקציונליות. מה שזה אומר למשתמש הוא שכל עוזרי המכשירים במערכת יעשו זאת
יש את כל שיטות ה-pcap trace זמינות; והשיטות הללו יעבדו כולן באותה מידה
דרך התקנים אם המכשיר מיישם EnablePcapInternal נכונה.
פפ מעקב מכשיר עוֹזֵר שיטות
void EnablePcap (std::string prefix, Ptr שני,
bool promiscuous = false, bool explicitFilename = false);
void EnablePcap (std::string prefix, std::string ndName,
bool promiscuous = false, bool explicitFilename = false);
void EnablePcap (std::string prefix, NetDeviceContainer d,
bool promiscuous = false);
void EnablePcap (std::string prefix, NodeContainer n,
bool promiscuous = false);
void EnablePcap (std::string prefix, uint32_t nodeid, uint32_t deviceid,
bool promiscuous = false);
void EnablePcapAll (std::string prefix, bool promiscuous = false);
בכל אחת מהשיטות המוצגות לעיל, יש פרמטר ברירת מחדל שנקרא מופקר זֶה
ברירת המחדל לשווא. פרמטר זה מציין שאין לאסוף את העקבות
מצב מופקר. אם אתה רוצה שהעקבות שלך יכללו את כל התנועה שנראית על ידי המכשיר
(ואם המכשיר תומך במצב מופקר) פשוט הוסף פרמטר אמיתי לכל אחד מה-
שיחות למעלה. לדוגמה,:
Ptr nd;
...
helper.EnablePcap ("תחילית", nd, true);
יאפשר לכידת מצב מופקר על NetDevice שצוין על ידי nd.
שתי השיטות הראשונות כוללות גם פרמטר ברירת מחדל שנקרא שם קובץ מפורש זה יהיה
יידונו להלן.
מומלץ לעיין ב-Doxygen לשיעור PcapHelperForDevice כדי למצוא את הפרטים
של שיטות אלה; אבל לסיכום...
אתה יכול לאפשר מעקב אחר pcap בזוג מסוים של צומת/רשת-התקן על ידי מתן א
Ptr כדי EnablePcap שיטה. ה Ptr משתמע מאז התקן נטו
חייב להיות שייך בדיוק לאחד צומת. לדוגמה,:
Ptr nd;
...
helper.EnablePcap ("קידומת", nd);
אתה יכול לאפשר מעקב אחר pcap בזוג מסוים של צומת/רשת-התקן על ידי מתן א
std :: string מייצג מחרוזת שירות שם אובייקט ל-an EnablePcap שיטה. ה
Ptr מסתכל למעלה ממחרוזת השם. שוב, ה משתמע מאז ה
התקן נטו בשם חייב להיות שייך בדיוק לאחד צומת. לדוגמה,:
שמות::Add ("שרת" ...);
שמות::Add ("שרת/eth0" ...);
...
helper.EnablePcap ("קידומת", "שרת/ath0");
אתה יכול לאפשר מעקב אחר pcap על אוסף של זוגות של צומת/רשת-התקן על ידי מתן א
NetDeviceContainer. לכל אחד NetDevice במיכל הסוג מסומן. לכל אחד
מכשיר מהסוג המתאים (אותו הסוג שמנוהל על ידי עוזר המכשיר), איתור הוא
מופעל. שוב, ה משתמע מאחר שהמכשיר הרשת שנמצא חייב להיות שייך בדיוק ל
אחד צומת. לדוגמה,:
NetDeviceContainer d = ...;
...
helper.EnablePcap ("קידומת", ד);
אתה יכול לאפשר מעקב אחר pcap על אוסף של זוגות של צומת/רשת-התקן על ידי מתן א
NodeContainer. לכל אחד צומת ב NodeContainer שלו מחובר NetDevices חוזרים.
עבור כל NetDevice מצורף לכל צומת במיכל, סוג התקן זה
בָּדוּק. לכל מכשיר מהסוג המתאים (אותו הסוג שמנוהל על ידי המכשיר
עוזר), המעקב מופעל.:
NodeContainer n;
...
helper.EnablePcap ("קידומת", n);
אתה יכול לאפשר מעקב אחר pcap על בסיס מזהה צומת ומזהה מכשיר וכן עם מפורש
Ptr. כל צומת במערכת יש מזהה צומת שלם וכל מכשיר מחובר לצומת
יש מזהה מכשיר שלם.:
helper.EnablePcap ("קידומת", 21, 1);
לבסוף, אתה יכול להפעיל מעקב אחר pcap עבור כל המכשירים במערכת, עם אותו סוג כמו
שמנוהל על ידי עוזר המכשיר.:
helper.EnablePcapAll ("קידומת");
פפ מעקב מכשיר עוֹזֵר שם הקובץ בחירה
בתיאורי השיטה לעיל משתמע בנייה של שם קובץ שלם על ידי
שיטת היישום. לפי מוסכמה, עקבות pcap ב- ns-3 המערכת הם מהצורה
- id>- id>.pcap
כפי שהוזכר קודם, לכל צומת במערכת יהיה מזהה צומת שהוקצה למערכת; ו
לכל מכשיר יהיה אינדקס ממשק (נקרא גם מזהה מכשיר) ביחס לצומת שלו.
כברירת מחדל, אם כן, קובץ מעקב pcap שנוצר כתוצאה מהפעלת מעקב בקובץ הראשון
מכשיר של צומת 21 באמצעות הקידומת "קידומת" יהיה prefix-21-1.pcap.
אתה תמיד יכול להשתמש ב- ns-3 שירות שמות אובייקטים כדי להבהיר זאת. לדוגמה, אם
אתה משתמש בשירות שם האובייקט כדי להקצות את השם "שרת" לצומת 21, ה-pcap שנוצר
שם קובץ המעקב יהפוך אוטומטית, prefix-server-1.pcap ואם אתה גם מקצה את
שם "eth0" למכשיר, שם קובץ ה-pcap שלך יקלוט את זה אוטומטית ויהיה
נקרא prefix-server-eth0.pcap.
לבסוף, שתיים מהשיטות המוצגות לעיל:
void EnablePcap (std::string prefix, Ptr nd, bool promiscuous = false, bool explicitFilename = false);
void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = false);
יש פרמטר ברירת מחדל שנקרא שם קובץ מפורש. כאשר מוגדר כ-true, פרמטר זה
משבית את מנגנון השלמת שמות הקבצים האוטומטי ומאפשר לך ליצור מפורש
שם קובץ. אפשרות זו זמינה רק בשיטות המאפשרות מעקב אחר pcap ב-a
מכשיר יחיד.
למשל, על מנת לארגן שעוזר התקן ייצור pcap מופקר יחיד
קובץ לכידה בשם מסוים (my-pcap-file.pcap) במכשיר נתון, אפשר:
Ptr nd;
...
helper.EnablePcap ("my-pcap-file.pcap", nd, true, true);
1 נָכוֹן פרמטר מאפשר עקבות מצב מופקר והשני אומר לעוזר
לפרש את קידומת פרמטר כשם קובץ שלם.
אשי מעקב מכשיר עוזרים
ההתנהגות של עוזר עקבות ascii מיקסין דומה באופן מהותי לגרסת pcap.
הבט ב src/network/helper/trace-helper.h אם אתה רוצה לעקוב אחר הדיון
תוך כדי התבוננות בקוד אמיתי.
הכיתה AsciiTraceHelperForDevice מוסיף את הפונקציונליות ברמה גבוהה לשימוש ב-ascii
מעקב לכיתה מסייעת במכשיר. כמו במקרה של pcap, כל מכשיר חייב ליישם את א
שיטה וירטואלית יחידה שעברה בירושה מ-ascii trace מיקסין.:
ריק וירטואלי EnableAsciiInternal (Ptr stream, std::string prefix, Ptr nd) = 0;
החתימה של שיטה זו משקפת את ההשקפה הממוקדת במכשיר של המצב במצב זה
רָמָה; וגם העובדה שייתכן שהעוזר כותב לזרם פלט משותף. כל
השיטות הציבוריות הקשורות ל-ascii-trace שהועברו מהכיתה AsciiTraceHelperForDevice
לצמצם לקרוא לשיטת היישום התלויה במכשיר יחיד. לדוגמה, ה
שיטות עקבות ascii ברמה הנמוכה ביותר,:
void EnableAscii (std::string prefix, Ptr nd);
void EnableAscii (Ptr זרם, Ptr nd);
יקרא ליישום המכשיר של EnableAsciiInternal ישירות, מתן א
קידומת חוקית או זרם. כל שאר שיטות המעקב הציבוריות של ascii יתבססו על אלה
פונקציות ברמה נמוכה כדי לספק פונקציונליות נוספת ברמת המשתמש. מה זה אומר ל
המשתמש הוא שלכל עוזרי המכשירים במערכת יהיו את כל שיטות המעקב של ascii
זמין; והשיטות הללו יפעלו כולן באותה צורה בכל המכשירים אם המכשירים
ליישם EnablAsciiInternal נכונה.
אשי מעקב מכשיר עוֹזֵר שיטות
void EnableAscii (std::string prefix, Ptr nd);
void EnableAscii (Ptr זרם, Ptr nd);
void EnableAscii (std::string prefix, std::string ndName);
void EnableAscii (Ptr stream, std::string ndName);
void EnableAscii (std::string prefix, NetDeviceContainer d);
void EnableAscii (Ptr stream, NetDeviceContainer d);
void EnableAscii (std::string prefix, NodeContainer n);
void EnableAscii (Ptr stream, NodeContainer n);
void EnableAscii (std::string prefix, uint32_t nodeid, uint32_t deviceid);
void EnableAscii (Ptr stream, uint32_t nodeid, uint32_t deviceid);
void EnableAsciiAll (קידומת std::string);
void EnableAsciiAll (Ptr זרם);
מומלץ לעיין ב-Doxygen לשיעור TraceHelperForDevice כדי למצוא את
פירוט של שיטות אלה; אבל לסיכום...
קיימות פי שניים שיטות זמינות למעקב אחר ascii מאשר ל-pcap
מַעֲקָב. הסיבה לכך היא, בנוסף לדגם pcap בסגנון שבו עקבות מכל אחד
צמד צומת/מכשיר ייחודי נכתב לקובץ ייחודי, אנו תומכים במודל שבו מעקב
מידע עבור זוגות רבים של צומת/מכשיר נכתב לקובץ משותף. המשמעות היא שה
- - מנגנון יצירת שם קובץ מוחלף במנגנון ל
עיין בקובץ נפוץ; ומספר שיטות ה-API מוכפל כדי לאפשר את כולם
שילובים.
בדיוק כמו ב-pcap tracing, אתה יכול לאפשר מעקב ascii בזוג מסוים של צומת/רשת-התקן
על ידי מתן א Ptr כדי EnableAscii שיטה. ה Ptr משתמע מאז
מכשיר הרשת חייב להיות שייך בדיוק לאחד צומת. לדוגמה,:
Ptr nd;
...
helper.EnableAscii ("תחילית", nd);
במקרה זה, לא נכתבים הקשרי מעקב לקובץ המעקב של ascii שכן הם יהיו
מיותר. המערכת תבחר את שם הקובץ שייווצר תוך שימוש באותם כללים כמו
המתואר בסעיף pcap, אלא שהקובץ יקבל את הסיומת ".tr" במקום
".pcap".
אם ברצונך לאפשר מעקב אחר ascii ביותר ממכשיר רשת אחד ושכל העקבות יישלחו
לקובץ בודד, אתה יכול לעשות זאת גם על ידי שימוש באובייקט כדי להתייחס לקובץ בודד:
Ptr nd1;
Ptr nd2;
...
Ptr stream = asciiTraceHelper.CreateFileStream ("Trace-file-name.tr");
...
helper.EnableAscii (זרם, nd1);
helper.EnableAscii (זרם, nd2);
במקרה זה, הקשרי מעקב נכתבים לקובץ המעקב של ascii מכיוון שהם נדרשים
כדי לבלבל עקבות משני המכשירים. שים לב כי מאז המשתמש הוא לחלוטין
תוך ציון שם הקובץ, המחרוזת צריכה לכלול את ה-".tr" למען עקביות.
אתה יכול לאפשר מעקב אחר ascii בזוג מסוים של צומת/התקן רשת על ידי מתן א
std :: string מייצג מחרוזת שירות שם אובייקט ל-an EnablePcap שיטה. ה
Ptr מסתכל למעלה ממחרוזת השם. שוב, ה משתמע מאז ה
התקן נטו בשם חייב להיות שייך בדיוק לאחד צומת. לדוגמה,:
שמות::Add ("לקוח" ...);
שמות::Add ("client/eth0" ...);
שמות::Add ("שרת" ...);
שמות::Add ("שרת/eth0" ...);
...
helper.EnableAscii ("תחילית", "client/eth0");
helper.EnableAscii ("קידומת", "שרת/eth0");
זה יביא לשני קבצים בשם תחילית-client-eth0.tr ו prefix-server-eth0.tr עם
עקבות עבור כל מכשיר בקובץ המעקב המתאים. מאז כל ה-EnableAscii
הפונקציות עומסות יתר על המידה כדי לקחת מעטפת זרם, אתה יכול להשתמש גם בטופס הזה:
שמות::Add ("לקוח" ...);
שמות::Add ("client/eth0" ...);
שמות::Add ("שרת" ...);
שמות::Add ("שרת/eth0" ...);
...
Ptr stream = asciiTraceHelper.CreateFileStream ("Trace-file-name.tr");
...
helper.EnableAscii (זרם, "client/eth0");
helper.EnableAscii (זרם, "שרת/eth0");
זה יביא לקובץ מעקב בודד שנקרא trace-file-name.tr שמכיל את כל
אירועי המעקב עבור שני המכשירים. האירועים יהיו מעורפלים על ידי הקשר עקבות
מיתרים.
אתה יכול לאפשר מעקב אחר ascii באוסף של זוגות של צומת/רשת-התקן על ידי מתן
NetDeviceContainer. לכל אחד NetDevice במיכל הסוג מסומן. לכל אחד
מכשיר מהסוג המתאים (אותו הסוג שמנוהל על ידי עוזר המכשיר), איתור הוא
מופעל. שוב, ה משתמע מאחר שהמכשיר הרשת שנמצא חייב להיות שייך בדיוק ל
אחד צומת. לדוגמה,:
NetDeviceContainer d = ...;
...
helper.EnableAscii ("תחילית", ד);
זה יביא ליצירת מספר קובצי מעקב של ascii, שכל אחד מהם יבוא בהמשך
ה - - אמנת .tr. שילוב כל העקבות לכדי א
קובץ בודד מתבצע בדומה לדוגמאות לעיל:
NetDeviceContainer d = ...;
...
Ptr stream = asciiTraceHelper.CreateFileStream ("Trace-file-name.tr");
...
helper.EnableAscii (זרם, ד);
אתה יכול לאפשר מעקב אחר ascii באוסף של זוגות של צומת/רשת-התקן על ידי מתן
NodeContainer. לכל אחד צומת ב NodeContainer שלו מחובר NetDevices חוזרים.
עבור כל NetDevice מצורף לכל צומת במיכל, סוג התקן זה
בָּדוּק. לכל מכשיר מהסוג המתאים (אותו הסוג שמנוהל על ידי המכשיר
עוזר), המעקב מופעל.:
NodeContainer n;
...
helper.EnableAscii ("תחילית", n);
זה יביא ליצירת מספר קובצי מעקב של ascii, שכל אחד מהם יבוא בהמשך
ה - - אמנת .tr. שילוב כל העקבות לכדי א
קובץ בודד מתבצע בדומה לדוגמאות לעיל:
אתה יכול לאפשר מעקב אחר pcap על בסיס מזהה צומת ומזהה מכשיר וכן עם מפורש
Ptr. כל צומת במערכת יש מזהה צומת שלם וכל מכשיר מחובר לצומת
יש מזהה מכשיר שלם.:
helper.EnableAscii ("תחילית", 21, 1);
כמובן שניתן לשלב את העקבות לקובץ בודד כפי שמוצג לעיל.
לבסוף, אתה יכול להפעיל מעקב אחר pcap עבור כל המכשירים במערכת, עם אותו סוג כמו
שמנוהל על ידי עוזר המכשיר.:
helper.EnableAsciiAll ("קידומת");
זה יביא ליצירת מספר קובצי מעקב ascii, אחד עבור כל מכשיר ב
המערכת מהסוג המנוהל על ידי העוזר. כל הקבצים האלה ילכו בעקבות ה
- - אמנת .tr. שילוב של כל העקבות ליחיד
הקובץ מתבצע בדומה לדוגמאות לעיל.
אשי מעקב מכשיר עוֹזֵר שם הקובץ בחירה
משתמע בתיאורי השיטה בסגנון הקידומת לעיל הוא בניית השלם
שמות קבצים לפי שיטת היישום. לפי מוסכמה, עקבות ascii ב- ns-3 מערכת הם
של הטופס - id>- id>.tr.
כפי שהוזכר קודם, לכל צומת במערכת יהיה מזהה צומת שהוקצה למערכת; ו
לכל מכשיר יהיה אינדקס ממשק (נקרא גם מזהה מכשיר) ביחס לצומת שלו.
כברירת מחדל, אם כן, קובץ מעקב ascii שנוצר כתוצאה מהפעלת מעקב בקובץ הראשון
התקן של צומת 21, באמצעות הקידומת "קידומת", יהיה קידומת-21-1.tr.
אתה תמיד יכול להשתמש ב- ns-3 שירות שמות אובייקטים כדי להבהיר זאת. לדוגמה, אם
אתה משתמש בשירות שם האובייקט כדי להקצות את השם "שרת" לצומת 21, וכתוצאה מכך
שם קובץ המעקב ascii יהפוך אוטומטית, prefix-server-1.tr ואם גם תקצה
השם "eth0" למכשיר, שם קובץ המעקב של ascii שלך יקלוט את זה באופן אוטומטי
ולהיקרא prefix-server-eth0.tr.
פפ מעקב פרוטוקול עוזרים
המטרה של אלה מיקסינס היא להקל על הוספת מתקן עקבות pcap עקבי
פרוטוקולים. אנחנו רוצים שכל הטעמים השונים של ה-pcap tracing יפעלו אותו הדבר בכל הטעמים
פרוטוקולים, כך שהשיטות של עוזרים אלה עוברות בירושה לעוזרי מחסנית. הבט ב
src/network/helper/trace-helper.h אם אתה רוצה לעקוב אחר הדיון תוך כדי התבוננות
קוד אמיתי.
בחלק זה נמחיש את השיטות כפי שיושלו על הפרוטוקול Ipv4. אל
ציין עקבות בפרוטוקולים דומים, פשוט החלף את הסוג המתאים. לדוגמה,
תשתמש ב Ptr במקום Ptr ואת השיחה EnablePcapIpv6 במקום EnablePcapIpv4.
הכיתה PcapHelperForIpv4 מספק את הפונקציונליות ברמה גבוהה לשימוש ב-pcap tracing
ב Ipv4 נוהל. כל עוזר פרוטוקול המאפשר שיטות אלה חייב ליישם יחיד
שיטה וירטואלית שעברה בירושה ממחלקה זו. יהיה יישום נפרד עבור
Ipv6, למשל, אבל ההבדל היחיד יהיה בשמות השיטה ובחתימות.
נדרשים שמות שיטות שונים כדי לבלבל מחלקה Ipv4 החל מ- Ipv6 אשר שניהם
נגזר מהכיתה חפץ, ושיטות שחולקות את אותה חתימה.:
ריק וירטואלי EnablePcapIpv4Internal (std::string prefix, Ptr ipv4, uint4_t interface) = 32;
החתימה של שיטה זו משקפת את הפרוטוקול והתצוגה הממוקדת בממשק של
המצב ברמה הזו. כל השיטות הציבוריות בירושה מהכיתה PcapHelperForIpv4
לצמצם לקרוא לשיטת היישום התלויה במכשיר הבודד הזו. לדוגמה, ה
שיטת pcap ברמה הנמוכה ביותר,:
void EnablePcapIpv4 (std::string prefix, Ptr ממשק ipv4, uint4_t);
יקרא ליישום המכשיר של EnablePcapIpv4Internal ישירות. כל שאר הציבור
שיטות מעקב pcap מבוססות על יישום זה כדי לספק רמת משתמש נוספת
פונקציונליות. המשמעות של זה למשתמש היא שכל עוזרי הפרוטוקול במערכת יעשו זאת
יש את כל שיטות ה-pcap trace זמינות; והשיטות הללו יעבדו כולן באותה מידה
דרך מעבר לפרוטוקולים אם העוזר מיישם EnablePcapIpv4Internal נכונה.
פפ מעקב פרוטוקול עוֹזֵר שיטות
שיטות אלו נועדו להיות בהתכתבות אחד לאחד עם צומת- ו
NetDevice- גרסאות ממוקדות של גרסאות המכשיר. במקום צומת ו NetDevice זוג
אילוצים, אנו משתמשים באילוצי פרוטוקול וממשק.
שימו לב שבדיוק כמו בגרסת המכשיר, יש שש שיטות:
void EnablePcapIpv4 (std::string prefix, Ptr ממשק ipv4, uint4_t);
void EnablePcapIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface);
void EnablePcapIpv4 (std::string prefix, Ipv4InterfaceContainer c);
void EnablePcapIpv4 (std::string prefix, NodeContainer n);
void EnablePcapIpv4 (std::string prefix, uint32_t nodeid, uint32_t interface);
void EnablePcapIpv4All (קידומת std::string);
מומלץ לעיין ב-Doxygen לשיעור PcapHelperForIpv4 כדי למצוא את הפרטים
של שיטות אלה; אבל לסיכום...
אתה יכול לאפשר מעקב אחר pcap בזוג פרוטוקול/ממשק מסוים על ידי מתן א
Ptr ו ממשק כדי EnablePcap שיטה. לדוגמה,:
Ptr ipv4 = node->GetObject ();
...
helper.EnablePcapIpv4 ("קידומת", ipv4, 0);
אתה יכול לאפשר מעקב אחר pcap בזוג מסוים של צומת/רשת-התקן על ידי מתן א
std :: string מייצג מחרוזת שירות שם אובייקט ל-an EnablePcap שיטה. ה
Ptr מסתכל למעלה ממחרוזת השם. לדוגמה,:
שמות::Add ("שרת IPv4" ...);
...
helper.EnablePcapIpv4 ("קידומת", "שרתIpv4", 1);
אתה יכול לאפשר מעקב אחר pcap על אוסף של זוגות פרוטוקול/ממשק על ידי מתן
Ipv4InterfaceContainer. לכל אחד Ipv4 / זוג ממשק במיכל את סוג הפרוטוקול
מסומן. עבור כל פרוטוקול מהסוג המתאים (אותו הסוג שמנוהל על ידי ה
מסייע המכשיר), מעקב מופעל עבור הממשק המתאים. לדוגמה,:
צמתים של NodeContainer;
...
התקני NetDeviceContainer = deviceHelper.Install (צמתים);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
ממשקי Ipv4InterfaceContainer = ipv4.Assign (התקנים);
...
helper.EnablePcapIpv4 ("קידומת", ממשקים);
אתה יכול לאפשר מעקב אחר pcap על אוסף של זוגות פרוטוקול/ממשק על ידי מתן א
NodeContainer. לכל אחד צומת ב NodeContainer נמצא הפרוטוקול המתאים. ל
כל פרוטוקול, הממשקים שלו מסופרים והמעקב מופעל על המתקבל
זוגות. לדוגמה,:
NodeContainer n;
...
helper.EnablePcapIpv4 ("קידומת", n);
אתה יכול לאפשר מעקב אחר pcap על בסיס מזהה הצומת והממשק גם כן. במקרה הזה,
ה-node-id מתורגם ל-a Ptr והפרוטוקול המתאים נבדק ב-
צוֹמֶת. הפרוטוקול והממשק המתקבלים משמשים לציון העקיבה המתקבלת
מָקוֹר.:
helper.EnablePcapIpv4 ("קידומת", 21, 1);
לבסוף, אתה יכול להפעיל מעקב אחר pcap עבור כל הממשקים במערכת, עם המשויכים
פרוטוקול זהה לזה המנוהל על ידי עוזר המכשיר.:
helper.EnablePcapIpv4All ("קידומת");
פפ מעקב פרוטוקול עוֹזֵר שם הקובץ בחירה
בכל תיאורי השיטה לעיל משתמע בניית השלם
שמות קבצים לפי שיטת היישום. לפי מוסכמה, עקבות pcap שנלקחו עבור מכשירים ב
מה היא ns-3 המערכת הם מהצורה - id>- id>.pcap. במקרה של
עקבות פרוטוקול, ישנה התכתבות אחד לאחד בין פרוטוקולים ו צמתים. זה
כי פרוטוקול אובייקטים מצטברים ל צומת אובייקטים. מכיוון שאין פרוטוקול גלובלי
id במערכת, אנו משתמשים במזהה הצומת המתאים במתן שם לקובץ. לכן יש א
אפשרות להתנגשויות שמות קבצים בשמות קבצי מעקב שנבחרו אוטומטית. לזה
הסיבה לכך, מוסכמות שמות הקובץ משתנה עבור מעקבי פרוטוקול.
כפי שהוזכר קודם, לכל צומת במערכת יהיה מזהה צומת שהוקצה למערכת.
מכיוון שישנה התאמה של אחד לאחד בין מופעי פרוטוקול למופעי צומת
אנו משתמשים במזהה הצומת. לכל ממשק יש מזהה ממשק ביחס לפרוטוקול שלו. אנו משתמשים
הכנס " -נ -אני .pcap" עבור מתן שם לקובץ מעקב
עוזרי פרוטוקול.
לכן, כברירת מחדל, קובץ מעקב pcap שנוצר כתוצאה מהפעלת מעקב
ממשק 1 של פרוטוקול Ipv4 של צומת 21 באמצעות הקידומת "קידומת" יהיה
"prefix-n21-i1.pcap".
אתה תמיד יכול להשתמש ב- ns-3 שירות שמות אובייקטים כדי להבהיר זאת. לדוגמה, אם
אתה משתמש בשירות שם האובייקט כדי להקצות את השם "serverIpv4" ל-Ptr על צומת
21, שם קובץ ה-pcap trace שהתקבל יהפוך אוטומטית,
"prefix-nserverIpv4-i1.pcap".
אשי מעקב פרוטוקול עוזרים
ההתנהגות של עוזרי עקבות ascii דומה באופן מהותי למקרה של pcap. קח א
מסתכלים src/network/helper/trace-helper.h אם אתה רוצה לעקוב אחר הדיון תוך כדי
מסתכל על קוד אמיתי.
בחלק זה נמחיש את השיטות כפי שיושלו על הפרוטוקול Ipv4. אל
ציין עקבות בפרוטוקולים דומים, פשוט החלף את הסוג המתאים. לדוגמה,
תשתמש ב Ptr במקום Ptr ואת השיחה EnableAsciiIpv6 במקום
EnableAsciiIpv4.
הכיתה AsciiTraceHelperForIpv4 מוסיף את הפונקציונליות ברמה גבוהה לשימוש ב-ascii
מעקב אל עוזר פרוטוקול. כל פרוטוקול המאפשר את השיטות הללו חייב ליישם את א
שיטה וירטואלית יחידה שעברה בירושה ממחלקה זו.:
ריק וירטואלי EnableAsciiIpv4Internal (Ptr stream, std::string prefix,
Ptr ipv4, uint4_t interface) = 32;
החתימה של שיטה זו משקפת את התצוגה הממוקדת בפרוטוקול ובממשק של
מצב ברמה זו; וגם העובדה שייתכן שהעוזר כותב למשותף
זרם פלט. כל השיטות הציבוריות בירושה מהכיתה
PcapAndAsciiTraceHelperForIpv4 לצמצם לקרוא את זה תלוי מכשיר יחיד
שיטת יישום. לדוגמה, שיטות המעקב של ascii ברמה הנמוכה ביותר,:
void EnableAsciiIpv4 (std::string prefix, Ptr ממשק ipv4, uint4_t);
void EnableAsciiIpv4 (Ptr זרם, Ptr ממשק ipv4, uint4_t);
יקרא ליישום המכשיר של EnableAsciiIpv4Internal באופן ישיר, מספק או
הקידומת או הזרם. כל שאר שיטות המעקב הציבוריות ל-ascii יתבססו על אלה
פונקציות ברמה נמוכה כדי לספק פונקציונליות נוספת ברמת המשתמש. מה זה אומר ל
המשתמש הוא שלכל עוזרי המכשירים במערכת יהיו את כל שיטות המעקב של ascii
זמין; והשיטות הללו יעבדו כולן באותו אופן על פני פרוטוקולים אם
פרוטוקולים ליישם EnablAsciiIpv4Internal נכונה.
אשי מעקב מכשיר עוֹזֵר שיטות
void EnableAsciiIpv4 (std::string prefix, Ptr ממשק ipv4, uint4_t);
void EnableAsciiIpv4 (Ptr זרם, Ptr ממשק ipv4, uint4_t);
void EnableAsciiIpv4 (std::string prefix, std::string ipv4Name, uint32_t interface);
void EnableAsciiIpv4 (Ptr stream, std::string ipv4Name, ממשק uint32_t);
void EnableAsciiIpv4 (std::string prefix, Ipv4InterfaceContainer c);
void EnableAsciiIpv4 (Ptr stream, Ipv4InterfaceContainer c);
void EnableAsciiIpv4 (std::string prefix, NodeContainer n);
void EnableAsciiIpv4 (Ptr stream, NodeContainer n);
void EnableAsciiIpv4 (std::string prefix, uint32_t nodeid, uint32_t deviceid);
void EnableAsciiIpv4 (Ptr stream, uint32_t nodeid, uint32_t interface);
void EnableAsciiIpv4All (קידומת std::string);
void EnableAsciiIpv4All (Ptr זרם);
מומלץ לעיין ב-Doxygen לשיעור PcapAndAsciiHelperForIpv4 כדי למצוא את
פירוט של שיטות אלה; אבל לסיכום...
קיימות פי שניים שיטות זמינות למעקב אחר ascii מאשר ל-pcap
מַעֲקָב. הסיבה לכך היא, בנוסף לדגם pcap בסגנון שבו עקבות מכל אחד
צמד פרוטוקול/ממשק ייחודי נכתבים לקובץ ייחודי, אנו תומכים במודל שבו
מידע מעקב עבור זוגות פרוטוקול/ממשק רבים נכתב לקובץ משותף. זֶה
אומר שה -נ - מנגנון יצירת שם קובץ מוחלף
על ידי מנגנון להתייחסות לקובץ משותף; ומספר שיטות ה-API מוכפל ל
אפשר את כל השילובים.
בדיוק כמו ב-pcap tracing, אתה יכול לאפשר מעקב ascii בפרוטוקול/ממשק מסוים
זוג על ידי מתן א Ptr ו ממשק כדי EnableAscii שיטה. לדוגמה,:
Ptr ipv4;
...
helper.EnableAsciiIpv4 ("קידומת", ipv4, 1);
במקרה זה, לא נכתבים הקשרי מעקב לקובץ המעקב של ascii שכן הם יהיו
מיותר. המערכת תבחר את שם הקובץ שייווצר תוך שימוש באותם כללים כמו
המתואר בסעיף pcap, אלא שהקובץ יקבל את הסיומת ".tr" במקום
".pcap".
אם אתה רוצה לאפשר מעקב ascii ביותר מממשק אחד ושכל העקבות יישלחו אל
קובץ בודד, אתה יכול לעשות זאת גם על ידי שימוש באובייקט כדי להתייחס לקובץ בודד. אָנוּ
יש כבר משהו דומה לזה בדוגמה של "cwnd" למעלה:
Ptr protocol4 = node1->GetObject ();
Ptr protocol4 = node2->GetObject ();
...
Ptr stream = asciiTraceHelper.CreateFileStream ("Trace-file-name.tr");
...
helper.EnableAsciiIpv4 (זרם, פרוטוקול1, 1);
helper.EnableAsciiIpv4 (זרם, פרוטוקול2, 1);
במקרה זה, הקשרי מעקב נכתבים לקובץ המעקב של ascii מכיוון שהם נדרשים
לבלבל עקבות משני הממשקים. שים לב כי מאז המשתמש הוא לחלוטין
תוך ציון שם הקובץ, המחרוזת צריכה לכלול את ה-".tr" למען עקביות.
אתה יכול לאפשר מעקב אחר ascii בפרוטוקול מסוים על ידי מתן א std :: string
מייצג מחרוזת שירות שם אובייקט ל-an EnablePcap שיטה. ה Ptr is
הרים את מבטו ממחרוזת השם. ה בשמות הקבצים המתקבלים משתמע מאז
ישנה התאמה אחד לאחד בין מופעי פרוטוקול וצמתים, לדוגמה:
שמות::Add ("node1Ipv4" ...);
שמות::Add ("node2Ipv4" ...);
...
helper.EnableAsciiIpv4 ("קידומת", "node1Ipv4", 1);
helper.EnableAsciiIpv4 ("קידומת", "node2Ipv4", 1);
זה יביא לשני קבצים בשם "prefix-nnode1Ipv4-i1.tr" ו
"prefix-nnode2Ipv4-i1.tr" עם עקבות עבור כל ממשק בקובץ המעקב המתאים.
מכיוון שכל הפונקציות של EnableAscii עומסות יתר על המידה כדי לקחת מעטפת זרם, אתה יכול
השתמש גם בטופס הזה:
שמות::Add ("node1Ipv4" ...);
שמות::Add ("node2Ipv4" ...);
...
Ptr stream = asciiTraceHelper.CreateFileStream ("Trace-file-name.tr");
...
helper.EnableAsciiIpv4 (זרם, "node1Ipv4", 1);
helper.EnableAsciiIpv4 (זרם, "node2Ipv4", 1);
זה יגרום לקובץ מעקב בודד בשם "trace-file-name.tr" המכיל את כל
אירועי המעקב עבור שני הממשקים. האירועים יהיו מעורפלים על ידי הקשר עקבות
מיתרים.
אתה יכול לאפשר מעקב אחר ascii על אוסף של זוגות פרוטוקול/ממשק על ידי מתן
Ipv4InterfaceContainer. עבור כל פרוטוקול מהסוג המתאים (אותו הסוג שמנוהל
על ידי עוזר המכשיר), מעקב מופעל עבור הממשק המתאים. שוב, ה
מרומז מאחר שיש התאמה אחד לאחד בין כל פרוטוקול ו
הצומת שלו. לדוגמה,:
צמתים של NodeContainer;
...
התקני NetDeviceContainer = deviceHelper.Install (צמתים);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
ממשקי Ipv4InterfaceContainer = ipv4.Assign (התקנים);
...
...
helper.EnableAsciiIpv4 ("קידומת", ממשקים);
זה יביא ליצירת מספר קובצי מעקב של ascii, שכל אחד מהם יבוא בהמשך
ה -נ -אני אמנת .tr. שילוב כל העקבות לכדי א
קובץ בודד מתבצע בדומה לדוגמאות לעיל:
צמתים של NodeContainer;
...
התקני NetDeviceContainer = deviceHelper.Install (צמתים);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
ממשקי Ipv4InterfaceContainer = ipv4.Assign (התקנים);
...
Ptr stream = asciiTraceHelper.CreateFileStream ("Trace-file-name.tr");
...
helper.EnableAsciiIpv4 (זרם, ממשקים);
אתה יכול לאפשר מעקב אחר ascii על אוסף של זוגות פרוטוקול/ממשק על ידי מתן א
NodeContainer. לכל אחד צומת ב NodeContainer נמצא הפרוטוקול המתאים. ל
כל פרוטוקול, הממשקים שלו מסופרים והמעקב מופעל על המתקבל
זוגות. לדוגמה,:
NodeContainer n;
...
helper.EnableAsciiIpv4 ("קידומת", n);
זה יביא ליצירת מספר קובצי מעקב של ascii, שכל אחד מהם יבוא בהמשך
ה - - אמנת .tr. שילוב כל העקבות לכדי א
קובץ בודד מתבצע בדומה לדוגמאות לעיל:
אתה יכול לאפשר מעקב אחר pcap על בסיס מזהה צומת ומזהה מכשיר גם כן. במקרה הזה,
ה-node-id מתורגם ל-a Ptr והפרוטוקול המתאים נבדק ב-
צוֹמֶת. הפרוטוקול והממשק המתקבלים משמשים לציון העקיבה המתקבלת
מָקוֹר.:
helper.EnableAsciiIpv4 ("קידומת", 21, 1);
כמובן שניתן לשלב את העקבות לקובץ בודד כפי שמוצג לעיל.
לבסוף, אתה יכול לאפשר מעקב אחר ascii עבור כל הממשקים במערכת, עם משויכים
פרוטוקול זהה לזה המנוהל על ידי עוזר המכשיר.:
helper.EnableAsciiIpv4All ("קידומת");
זה יביא ליצירת מספר קבצי מעקב ascii, אחד עבור כל ממשק
במערכת הקשורה לפרוטוקול מהסוג המנוהל על ידי העוזר. כל הקבצים האלה
יעקוב אחר -נ -אני
לקובץ בודד מתבצע בדומה לדוגמאות לעיל.
אשי מעקב מכשיר עוֹזֵר שם הקובץ בחירה
משתמע בתיאורי השיטה בסגנון הקידומת לעיל הוא בניית השלם
שמות קבצים לפי שיטת היישום. לפי מוסכמה, עקבות ascii ב- ns-3 מערכת הם
של הטופס" - - .tr."
כפי שהוזכר קודם, לכל צומת במערכת יהיה מזהה צומת שהוקצה למערכת.
מכיוון שיש התאמה של אחד לאחד בין פרוטוקולים וצמתים אנו משתמשים ל-node-id
כדי לזהות את זהות הפרוטוקול. לכל ממשק בפרוטוקול נתון יהיה
אינדקס ממשק (נקרא גם פשוט ממשק) ביחס לפרוטוקול שלו. כברירת מחדל,
לאחר מכן, קובץ מעקב ascii שנוצר כתוצאה מהפעלת מעקב במכשיר הראשון של
צומת 21, באמצעות הקידומת "קידומת", יהיה "תחילית-n21-i1.tr". השתמש בקידומת ל
לבטל פרוטוקולים מרובים לכל צומת.
אתה תמיד יכול להשתמש ב- ns-3 שירות שמות אובייקטים כדי להבהיר זאת. לדוגמה, אם
אתה משתמש בשירות שם האובייקט כדי להקצות את השם "serverIpv4" לפרוטוקול בצומת
21, וגם לציין ממשק אחד, שם הקובץ Ascii עקבות יהיה אוטומטית
להיות, "prefix-nserverIpv4-1.tr".
מעקב הפעלה פרטים
נתונים אוספים
פרק זה מתאר את מסגרת איסוף הנתונים ns-3 (DCF), המספקת
יכולות להשיג נתונים שנוצרו על ידי מודלים בסימולטור, לביצוע און ליין
הפחתה ועיבוד נתונים, וכן לרכז נתונים גולמיים או שעברו טרנספורמציה לתפוקות שונות
פורמטים.
המסגרת תומכת כיום בריצות עצמאיות של ns-3 שאינן מסתמכות על אף חיצוני
בקרת ביצוע תוכנית. האובייקטים שסופקו על ידי DCF עשויים להיות מחוברים אליהם ns-3 להתחקות
מקורות כדי לאפשר עיבוד נתונים.
קוד המקור של השיעורים נמצא בספרייה src/stats.
פרק זה מאורגן באופן הבא. ראשית, סקירה כללית של הארכיטקטורה היא
הוצג. בהמשך, מוצגים העוזרים לכיתות אלו; טיפול ראשוני זה
צריך לאפשר שימוש בסיסי במסגרת איסוף הנתונים עבור מקרי שימוש רבים. משתמשים אשר
רוצים לייצר פלט מחוץ לתחום העוזרים הנוכחיים, או שרוצים ליצור
אובייקטי איסוף הנתונים שלהם, צריכים לקרוא את המשך הפרק, אשר הולך
לפרטים על כל סוגי האובייקטים הבסיסיים של DCF ומספק קידוד ברמה נמוכה
דוגמאות.
עיצוב
DCF מורכב משלושה מחלקות בסיסיות:
· בדיקה הוא מנגנון למכשיר ובקרה על הפלט של נתוני סימולציה כלומר
משמש למעקב אחר אירועים מעניינים. זה מייצר פלט בצורה של אחד או יותר ns-3
מקורות עקבות. חפצי בדיקה מחוברים לעקבה אחת או יותר כיורים (שקוראים לו
אספנים), אשר מעבדים דוגמאות און ליין ומכינות אותן לפלט.
· אספן צורכת את הנתונים שנוצרו על ידי אובייקט Probe אחד או יותר. זה מבצע
טרנספורמציות על הנתונים, כגון נורמליזציה, הפחתה וחישוב של
סטטיסטיקה בסיסית. אובייקטי אספן אינם מייצרים נתונים המופקים ישירות על ידי ה
ריצת ns-3; במקום זאת, הם פלטים נתונים במורד הזרם לסוג אחר של אובייקט, הנקרא
צבר, שמבצע את הפונקציה הזו. בדרך כלל, אספנים מוציאים את הנתונים שלהם פנימה
גם בצורת מקורות עקבות, המאפשרים לכבול אספנים בסדרות.
· צבר היא נקודת הסיום של הנתונים שנאספו על ידי רשת של בדיקות ואספנים.
האחריות העיקרית של האגרגטור היא לרכז נתונים והתאמתם
מטא נתונים, לפורמטים שונים של פלט כגון קבצי טקסט רגיל, קבצי גיליונות אלקטרוניים, או
מאגרי מידע.
כל שלושת המחלקות הללו מספקות את היכולת להפעיל או לכבות את עצמם באופן דינמי
לאורך סימולציה.
כל עצמאי ns-3 ריצת סימולציה המשתמשת ב- DCF תיצור בדרך כלל לפחות אחד
מופע של כל אחת משלושת המחלקות לעיל.
[תמונה] סקירת מסגרת איסוף נתונים.UNINDENT
הזרימה הכוללת של עיבוד הנתונים מתוארת ב נתונים אוספים מסגרת סקירה.
בצד שמאל, ריצה ns-3 סימולציה מתוארת. במהלך הפעלת ה
סימולציה, הנתונים זמינים על ידי מודלים באמצעות מקורות עקבות, או באמצעים אחרים.
התרשים מתאר שניתן לחבר בדיקות למקורות עקבות אלה כדי לקבל נתונים
באופן אסינכרוני, או בדיקות יכולות לבצע סקר נתונים. לאחר מכן, הנתונים מועברים לאובייקט אספן
שהופך את הנתונים. לבסוף, ניתן לחבר אגרגטור ליציאות של
אספן, ליצירת מגרשים, קבצים או מסדי נתונים.
[תמונה] אגרגציה של מסגרת איסוף נתונים.UNINDENT
וריאציה על האיור לעיל מסופקת ב נתונים אוספים מסגרת - צבירה.
איור שני זה ממחיש שאובייקטי DCF עשויים להיות משורשרים יחדיו באופן
שאובייקטים במורד הזרם מקבלים תשומות ממספר אובייקטים במעלה הזרם. הצורה
מראה קונספטואלית שמספר בדיקות עשויות ליצור פלט שמוזן ליחידה
אַסְפָן; כדוגמה, אספן שמפיק יחס של שני מונים יעשה זאת
בדרך כלל רוכשים כל נתוני מונה מבדיקות נפרדות. אספנים מרובים יכולים גם כן
הזנה לתוך אגרגטור יחיד, אשר (כשמו כן הוא) עשוי לאסוף מספר נתונים
זרמים להכללה בחלקה, קובץ או מסד נתונים בודדים.
נתונים אוספים עוזרים
הגמישות המלאה של מסגרת איסוף הנתונים מסופקת על ידי הקישוריות
של בדיקות, אספנים ואגרגטורים. ביצוע כל החיבורים הללו מוביל ל
הצהרות תצורה רבות בתוכניות משתמש. לנוחות השימוש, חלק מהנפוצים ביותר
ניתן לשלב פעולות ולכלול אותן בפונקציות עוזר. בנוסף, כמה
אמירות המעורבות ns-3 למקורות עקבות אין כריכות Python, עקב מגבלות ב
הכריכות.
נתונים אוספים עוזרים סקירה כללית
בחלק זה, אנו מספקים סקירה כללית של כמה כיתות עוזר שנוצרו עבור
להקל על התצורה של מסגרת איסוף הנתונים עבור כמה מקרי שימוש נפוצים. ה
עוזרים מאפשרים למשתמשים ליצור פעולות נפוצות עם רק כמה הצהרות ב-C++ או
תוכניות פייתון. אבל, קלות השימוש הזו באה במחיר של פחות משמעותית
גמישות מאשר תצורה ברמה נמוכה יכולה לספק, והצורך בקוד מפורש
תמיכה בסוגי בדיקה חדשים לתוך העוזרים (כדי לעקוף בעיה המתוארת להלן).
הדגש על העוזרים הנוכחיים הוא לסדר נתונים ns-3 להתחקות אחר מקורות לתוך
עלילות gnuplot או קבצי טקסט, ללא רמה גבוהה של התאמה אישית של פלט או סטטיסטיקה
עיבוד (בהתחלה). כמו כן, השימוש מוגבל לסוגי הבדיקה הזמינים ב
ns-3. חלקים מאוחרים יותר בתיעוד זה יפרטו יותר על יצירת חדש
סוגי בדיקה, כמו גם פרטים על חיבור בדיקות, אספנים ואגרגטורים
בסידורים מותאמים אישית.
עד כה, שני עוזרי איסוף נתונים יושמו:
· GnuplotHelper
· FileHelper
GnuplotHelper
ה-GnuplotHelper הוא מחלקה עוזרת להפקת קבצי פלט המשמשים לייצור גנופלוטים. ה
המטרה הכללית היא לספק למשתמשים את היכולת ליצור במהירות עלילות מנתונים מיוצאים
in ns-3 מקורות עקבות. כברירת מחדל, מתבצעת כמות מינימלית של שינוי נתונים;
המטרה היא ליצור מגרשים עם כמה שפחות הצהרות תצורה (ברירת מחדל).
אפשרי.
GnuplotHelper סקירה כללית
ה-GnuplotHelper יצור 3 קבצים שונים בסוף הסימולציה:
· קובץ נתוני gnuplot מופרדים מרווחים
· קובץ בקרת גנופלוט
· סקריפט מעטפת ליצירת הגנופלוט
ישנן שתי הצהרות תצורה שדרושות להפקת עלילות. הראשון
המשפט מגדיר את העלילה (שם קובץ, כותרת, אגדות וסוג פלט, שבו הפלט
סוג ברירת המחדל ל-PNG אם לא צוין):
void ConfigurePlot (const std::string &outputFileNameWithoutExtension,
const std::string &title,
const std::string &xLegend,
const std::string &yLegend,
const std::string &terminalType = ".png");
ההצהרה השנייה תופסת את מקור העניין:
void PlotProbe (const std::string &typeId,
const std::string &path,
const std::string &probeTraceSource,
const std::string &title);
הטיעונים הם כדלקמן:
· typeId: ה ns-3 TypeId של הבדיקה
· נתיב: השביל ב- ns-3 מרחב שמות של תצורה למקור מעקב אחד או יותר
· probeTraceSource: איזה פלט של הבדיקה (בעצמה מקור עקבות) צריך להיות משורטט
· כותרת: הכותרת שיש לשייך למערך הנתונים (במקרא ה-gnuplot)
גרסה של PlotProbe לעיל היא לציין ארגומנט אופציונלי חמישי השולט
היכן בעלילה ממוקם המפתח (אגדה).
דוגמה מעובדת במלואה (מ seventh.cc) מוצג להלן:
// צור את עוזר הגנופלוט.
GnuplotHelper plotHelper;
// הגדר את העלילה.
// הגדר את העלילה. הארגומנט הראשון הוא קידומת שם הקובץ
// עבור קבצי הפלט שנוצרו. השני, השלישי והרביעי
// ארגומנטים הם, בהתאמה, כותרת העלילה, תוויות ציר ה-x וציר ה-y
plotHelper.ConfigurePlot ("ספירת-חבילה-שביעית-בתים",
"ספירת בתים של חבילה לעומת זמן",
"זמן (שניות)",
"ספירת בתים של חבילה",
"png");
// ציין את סוג הבדיקה, נתיב המקור (במרחב השמות של התצורה), וכן
// בדיקה מקור עקבות פלט ("OutputBytes") כדי לתכנן. הטיעון הרביעי
// מציין את השם של תווית סדרת הנתונים על העלילה. האחרון
// ארגומנט מעצב את העלילה על ידי ציון היכן יש למקם את המפתח.
plotHelper.PlotProbe (probeType,
tracePath,
"OutputBytes",
"ספירת בתים של חבילה",
GnuplotAggregator::KEY_BELOW);
בדוגמה זו, probeType ו tracePath הם כדלקמן (עבור IPv4):
probeType = "ns3::Ipv4PacketProbe";
tracePath = "/NodeList/*/$ns3::Ipv4L3Protocol/Tx";
ה-probeType הוא פרמטר מפתח כדי שהעוזר הזה יעבוד. TypeId זה חייב להיות רשום
במערכת, והחתימה על כיור העקיבה של ה-Probe חייבת להתאים לזו של העקיבה
מקור אליו הוא מחובר. סוגי בדיקה מוגדרים מראש עבור מספר סוגי נתונים
המקביל ל ns-3 ערכי מעקב, ועבור כמה חתימות אחרות של מקור מעקב כגון
מקור העקבות 'Tx' של ns3::Ipv4L3Protocol מעמד.
שים לב שנתיב מקור המעקב שצוין עשוי להכיל תווים כלליים. במקרה זה, מרובה
מערכי נתונים משורטטים על חלקה אחת; אחד עבור כל נתיב מותאם.
הפלט העיקרי שיופק יהיה שלושה קבצים:
seventh-packet-byte-count.dat
seventh-packet-byte-count.plt
seventh-packet-byte-count.sh
בשלב זה, משתמשים יכולים לערוך ביד את קובץ ה-.plt עבור התאמות אישיות נוספות, או
פשוט הפעל את זה דרך גנופלוט. רץ sh seventh-packet-byte-count.sh פשוט מנהל את העלילה
דרך gnuplot, כפי שמוצג להלן.
[תמונה] 2-D Gnuplot נוצר על ידי seventh.cc דוגמה..UNINDENT
ניתן לראות שמרכיבי המפתח (אגדה, כותרת, מיקום מקרא, xlabel, ylabel,
והנתיב לנתונים) ממוקמים כולם על העלילה. מאז היו שני התאמות ל
נתיב תצורה מסופק, שתי סדרות הנתונים מוצגות:
· ספירת בתים של מנות-0 מתאימה ל-/NodeList/0/$ns3::Ipv4L3Protocol/Tx
· ספירת בתים של מנות-1 מתאימה ל-/NodeList/1/$ns3::Ipv4L3Protocol/Tx
GnuplotHelper ConfigurePlot
ה-GnuplotHelper's ConfigurePlot() ניתן להשתמש בפונקציה כדי להגדיר מגרשים.
יש לו את אב הטיפוס הבא:
void ConfigurePlot (const std::string &outputFileNameWithoutExtension,
const std::string &title,
const std::string &xLegend,
const std::string &yLegend,
const std::string &terminalType = ".png");
יש לו את הטיעונים הבאים:
┌───────────────────────────────┬───────────────── ─────────────────┐
│טיעון │ תיאור │
├───────────────────────────────┼───────────────── ─────────────────┤
│outputFileNameWithoutExtension │ שם של קבצים הקשורים ל-gnuplot אל │
│ │ כתוב ללא סיומת. │
├───────────────────────────────┼───────────────── ─────────────────┤
│כותרת │ מחרוזת כותרת עלילה לשימוש עבור │
│ │ העלילה הזו. │
├───────────────────────────────┼───────────────── ─────────────────┤
│xLegend │ המקרא עבור ה-x האופקי │
ציר │ │. │
├───────────────────────────────┼───────────────── ─────────────────┤
│yLegend │ המקרא עבור אנכי y │
ציר │ │. │
└───────────────────────────────┴───────────────── ─────────────────┘
│terminalType │ מחרוזת הגדרת סוג טרמינל עבור │
│ │ פלט. מסוף ברירת המחדל │
סוג │ │ הוא "png". │
└───────────────────────────────┴───────────────── ─────────────────┘
ה-GnuplotHelper's ConfigurePlot() הפונקציה מגדירה פרמטרים הקשורים לעלילה עבור זה
gnuplot helper כך שהוא יצור קובץ נתוני gnuplot מופרד מרווח בשם
outputFileNameWithoutExtension + ".dat", קובץ בקרת gnuplot בשם
outputFileNameWithoutExtension + ".plt", וסקריפט מעטפת ליצירת הגנופלוט בשם
outputFileNameWithoutExtension + ".sh".
דוגמה לשימוש בפונקציה זו ניתן לראות ב- seventh.cc קוד שתואר לעיל
שבו נעשה בו שימוש באופן הבא:
plotHelper.ConfigurePlot ("ספירת-חבילה-שביעית-בתים",
"ספירת בתים של חבילה לעומת זמן",
"זמן (שניות)",
"ספירת בתים של חבילה",
"png");
GnuplotHelper PlotProbe
ה-GnuplotHelper's PlotProbe() ניתן להשתמש בפונקציה כדי לשרטט ערכים שנוצרו על ידי בדיקות.
יש לו את אב הטיפוס הבא:
void PlotProbe (const std::string &typeId,
const std::string &path,
const std::string &probeTraceSource,
const std::string &title,
enum GnuplotAggregator::KeyLocation keyLocation = GnuplotAggregator::KEY_INSIDE);
יש לו את הטיעונים הבאים:
┌─────────────────┬─────────────────────────────── ───┐
│טיעון │ תיאור │
├─────────────────┼─────────────────────────────── ───┤
│typeId │ מזהה הסוג של הבדיקה │
│ │ נוצר על ידי עוזר זה. │
├─────────────────┼─────────────────────────────── ───┤
│נתיב │ הגדר נתיב לגישה למעקב │
│ │ מקור. │
├─────────────────┼─────────────────────────────── ───┤
│probeTraceSource │ מקור מעקב הבדיקה אל │
│ │ גישה. │
├─────────────────┼─────────────────────────────── ───┤
│כותרת │ הכותרת שיש לשייך אליה │
│ │ מערך הנתונים הזה │
├─────────────────┼─────────────────────────────── ───┤
│keyLocation │ מיקום המפתח ב│
│ │ עלילה. מיקום ברירת המחדל הוא │
│ │ בפנים. │
└─────────────────┴─────────────────────────────── ───┘
ה-GnuplotHelper's PlotProbe() function משרטט מערך נתונים שנוצר על ידי חיבור של ns-3
התחקות אחר מקור עם בדיקה שנוצרה על ידי העוזר, ולאחר מכן מתווה את הערכים מה-
probeTraceSource. למערך הנתונים תהיה הכותרת שסופקה, והוא יהיה מורכב מה-
'newValue' בכל חותמת זמן.
אם לנתיב התצורה יש יותר מהתאמה אחת במערכת בגלל שיש תו כללי, אז
מערך נתונים אחד עבור כל התאמה ישורטט. כותרות מערך הנתונים יסתיימו ב-
תווים מותאמים לכל אחד מהתווים הכלליים בנתיב התצורה, מופרדים ברווחים. ל
לדוגמה, אם כותרת הנתונים המוצעים היא המחרוזת "בתים", ויש שני תווים כלליים לחיפוש
בנתיב, אז כותרות מערך נתונים כמו "bytes-0 0" או "bytes-12 9" יהיו אפשריים בתור
תוויות עבור מערכי הנתונים המתוכננים.
דוגמה לשימוש בפונקציה זו ניתן לראות ב- seventh.cc קוד שתואר לעיל
היכן הוא שימש (עם החלפה משתנה) באופן הבא:
plotHelper.PlotProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"OutputBytes",
"ספירת בתים של חבילה",
GnuplotAggregator::KEY_BELOW);
אחר דוגמאות
גנופלוט עוֹזֵר דוגמה
דוגמה קצת יותר פשוטה מה seventh.cc ניתן למצוא דוגמה ב
src/stats/examples/gnuplot-helper-example.cc. הגנופלוט הדו-ממדי הבא נוצר באמצעות
הדוגמה.
[תמונה] Gnuplot דו-ממדי נוצר על ידי gnuplot-helper-example.cc דוגמה..UNINDENT
בדוגמה זו, יש אובייקט פולט שמגדיל את המונה שלו לפי א
Poisson מעבד ולאחר מכן פולט את הערך של המונה כמקור עקבות.
Ptr emitter = CreateObject ();
שמות::Add ("/שמות/פולט", פולט);
שים לב שבגלל שאין תווים כלליים בנתיב המשמש למטה, רק זרם נתונים אחד היה
מצוייר בעלילה. זרם הנתונים הבודד הזה בעלילה נקרא פשוט "ספירת פולטים",
ללא סיומות נוספות כמו שאפשר לראות אם יש תווים כלליים בנתיב.
// צור את עוזר הגנופלוט.
GnuplotHelper plotHelper;
// הגדר את העלילה.
plotHelper.ConfigurePlot ("gnuplot-helper-example",
"ספירת פולטים לעומת זמן",
"זמן (שניות)",
"ספירת פולטים",
"png");
// ציירו את הערכים שנוצרו על ידי הגשושית. הדרך שאנו מספקים
// עוזר לבלבל את מקור העקבות.
plotHelper.PlotProbe ("ns3::Uinteger32Probe",
"/שמות/פולט/מונה",
"תְפוּקָה",
"ספירת פולטים",
GnuplotAggregator::KEY_INSIDE);
FileHelper
ה-FileHelper הוא מחלקת עוזר המשמשת להכנסת ערכי נתונים לקובץ. המטרה הכללית היא
כדי לספק למשתמשים את היכולת ליצור במהירות קבצי טקסט מעוצבים מנתונים מיוצאים
in ns-3 מקורות עקבות. כברירת מחדל, מתבצעת כמות מינימלית של שינוי נתונים;
המטרה היא ליצור קבצים עם כמה שפחות הצהרות תצורה (ברירת מחדל).
אפשרי.
FileHelper סקירה כללית
ה-FileHelper יצור קובץ טקסט אחד או יותר בסוף הסימולציה.
ה-FileHelper יכול ליצור 4 סוגים שונים של קבצי טקסט:
· מעוצב
· רווח מופרד (ברירת המחדל)
· מופרד באמצעות פסיקים
· כרטיסיות מופרדות
קבצים מעוצבים משתמשים במחרוזות פורמט בסגנון C ובפונקציה sprintf() כדי להדפיס אותם
ערכים בקובץ הנכתב.
קובץ הטקסט הבא עם 2 עמודות של ערכים מעוצבים בשם
seventh-packet-byte-count-0.txt נוצר באמצעות קוד חדש נוסף שהתווסף ל-
מְקוֹרִי ns-3 קוד לדוגמה של הדרכה. רק 10 השורות הראשונות של קובץ זה מוצגות
כאן לקיצור.
זמן (שניות) = 1.000e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.004e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.004e+00 ספירת בתים של מנות = 576
זמן (שניות) = 1.009e+00 ספירת בתים של מנות = 576
זמן (שניות) = 1.009e+00 ספירת בתים של מנות = 576
זמן (שניות) = 1.015e+00 ספירת בתים של מנות = 512
זמן (שניות) = 1.017e+00 ספירת בתים של מנות = 576
זמן (שניות) = 1.017e+00 ספירת בתים של מנות = 544
זמן (שניות) = 1.025e+00 ספירת בתים של מנות = 576
זמן (שניות) = 1.025e+00 ספירת בתים של מנות = 544
...
קובץ הטקסט השונה הבא עם 2 עמודות של ערכים מעוצבים בשם
seventh-packet-byte-count-1.txt נוצר גם באמצעות אותו קוד חדש שנוסף אליו
המקורי ns-3 קוד לדוגמה של הדרכה. רק 10 השורות הראשונות של קובץ זה מוצגות
כאן לקיצור.
זמן (שניות) = 1.002e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.007e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.013e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.020e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.028e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.036e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.045e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.053e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.061e+00 ספירת בתים של מנות = 40
זמן (שניות) = 1.069e+00 ספירת בתים של מנות = 40
...
הקוד החדש שנוסף להפקת שני קבצי הטקסט נמצא למטה. פרטים נוספים על
ממשק API זה יסוקר בסעיף מאוחר יותר.
שימו לב שבגלל שהיו 2 התאמות לתווים הכלליים בנתיב, 2 קובצי טקסט נפרדים
נוצרו. קובץ הטקסט הראשון, ששמו "seventh-packet-byte-count-0.txt",
מתאים להתאמה של התווים הכלליים כשה-"*" מוחלף ב-"0". קובץ הטקסט השני,
ששמו "seventh-packet-byte-count-1.txt", מתאים להתאמה של התווים הכלליים עם
ה-"*" הוחלף ב-"1". כמו כן, שימו לב שהפונקציה קוראת ל WriteProbe() ייתן
הודעת שגיאה אם אין התאמות לנתיב המכיל תווים כלליים.
// צור את עוזר הקובץ.
FileHelper fileHelper;
// הגדר את הקובץ שייכתב.
fileHelper.ConfigureFile ("ספירת-חבילה-שביעית-בתים",
FileAggregator::FORMATTED);
// הגדר את התוויות עבור קובץ הפלט המעוצב הזה.
fileHelper.Set2dFormat ("זמן (שניות) = %.3e\tPacket Byte Count = %.0f");
// כתוב את הערכים שנוצרו על ידי הבדיקה.
fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"OutputBytes");
FileHelper ConfigureFile
של ה-FileHelper ConfigureFile() ניתן להשתמש בפונקציה כדי להגדיר קבצי טקסט.
יש לו את אב הטיפוס הבא:
void ConfigureFile (const std::string &outputFileNameWithoutExtension,
enum FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);
יש לו את הטיעונים הבאים:
┌───────────────────────────────┬───────────────── ─────────────────┐
│טיעון │ תיאור │
├───────────────────────────────┼───────────────── ─────────────────┤
│outputFileNameWithoutExtension │ שם קובץ הפלט לכתיבה │
│ │ ללא הרחבה. │
├───────────────────────────────┼───────────────── ─────────────────┤
│fileType │ סוג הקובץ לכתיבה. ה│
│ │ סוג ברירת המחדל של הקובץ הוא רווח │
│ │ מופרדים. │
└───────────────────────────────┴───────────────── ─────────────────┘
של ה-FileHelper ConfigureFile() הפונקציה מגדירה פרמטרים הקשורים לקובץ טקסט עבור
עוזר קובץ כך שהוא יצור קובץ בשם outputFileNameWithoutExtension plus
מידע נוסף אפשרי מהתאמות עם תווים כלליים בתוספת ".txt" עם ערכים מודפסים כ
שצוין על ידי fileType. סוג הקובץ המוגדר כברירת מחדל הוא מופרד מרווחים.
דוגמה לשימוש בפונקציה זו ניתן לראות ב- seventh.cc קוד שתואר לעיל
שבו נעשה בו שימוש באופן הבא:
fileHelper.ConfigureFile ("ספירת-חבילה-שביעית-בתים",
FileAggregator::FORMATTED);
FileHelper WriteProbe
של ה-FileHelper WriteProbe() ניתן להשתמש בפונקציה כדי לכתוב ערכים שנוצרו על ידי בדיקות ל
קבצי טקסט.
יש לו את אב הטיפוס הבא:
void WriteProbe (const std::string &typeId,
const std::string &path,
const std::string &probeTraceSource);
יש לו את הטיעונים הבאים:
┌─────────────────┬─────────────────────────────── ───┐
│טיעון │ תיאור │
├─────────────────┼─────────────────────────────── ───┤
│typeId │ מזהה הסוג עבור הגשוש להיות │
│ │ נוצר. │
├─────────────────┼─────────────────────────────── ───┤
│נתיב │ הגדר נתיב לגישה למעקב │
│ │ מקור. │
├─────────────────┼─────────────────────────────── ───┤
│probeTraceSource │ מקור מעקב הבדיקה אל │
│ │ גישה. │
└─────────────────┴─────────────────────────────── ───┘
של ה-FileHelper WriteProbe() הפונקציה יוצרת קובצי טקסט פלט שנוצרו על ידי חיבור של
מקור התחקות ns-3 עם בדיקה שנוצרה על ידי העוזר, ולאחר מכן כתיבת הערכים מה-
probeTraceSource. שמות קבצי הפלט יאוחסנו במשתנה האיבר
m_outputFileNameWithoutExtension בתוספת ".txt", והוא יהיה מורכב מה-newValue בכל אחד
חותמת זמן.
אם לנתיב התצורה יש יותר מהתאמה אחת במערכת בגלל שיש תו כללי, אז
יווצר קובץ פלט אחד עבור כל התאמה. שמות קבצי הפלט יכילו את
טקסט ב-m_outputFileNameWithoutExtension בתוספת התווים התואמים עבור כל אחד מה-
תווים כלליים בנתיב התצורה, מופרדים באמצעות מקפים, בתוספת ".txt". לדוגמה, אם הערך
ב-m_outputFileNameWithoutExtension היא המחרוזת "packet-byte-count", ויש שניים
תווים כלליים בנתיב, ואז פלט שמות קבצים כמו "packet-byte-count-0-0.txt" או
"packet-byte-count-12-9.txt" יהיה אפשרי בתור שמות לקבצים שייווצרו.
דוגמה לשימוש בפונקציה זו ניתן לראות ב- seventh.cc קוד שתואר לעיל
שבו נעשה בו שימוש באופן הבא:
fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"OutputBytes");
אחר דוגמאות
שלח עוֹזֵר דוגמה
דוגמה קצת יותר פשוטה מה seventh.cc ניתן למצוא דוגמה ב
src/stats/examples/file-helper-example.cc. דוגמה זו משתמשת רק ב- FileHelper.
קובץ הטקסט הבא עם 2 עמודות של ערכים מעוצבים בשם file-helper-example.txt
נוצר באמצעות הדוגמה. רק 10 השורות הראשונות של קובץ זה מוצגות כאן עבור
קוֹצֶר.
זמן (שניות) = 0.203 ספירה = 1
זמן (שניות) = 0.702 ספירה = 2
זמן (שניות) = 1.404 ספירה = 3
זמן (שניות) = 2.368 ספירה = 4
זמן (שניות) = 3.364 ספירה = 5
זמן (שניות) = 3.579 ספירה = 6
זמן (שניות) = 5.873 ספירה = 7
זמן (שניות) = 6.410 ספירה = 8
זמן (שניות) = 6.472 ספירה = 9
...
בדוגמה זו, יש אובייקט פולט שמגדיל את המונה שלו לפי א
Poisson מעבד ולאחר מכן פולט את הערך של המונה כמקור עקבות.
Ptr emitter = CreateObject ();
שמות::Add ("/שמות/פולט", פולט);
שים לב שבגלל שאין תווים כלליים בנתיב המשמש למטה, רק קובץ טקסט אחד היה
נוצר. קובץ הטקסט היחיד הזה נקרא בפשטות "file-helper-example.txt", ללא תוספת
סיומות כמו שהיית רואה אם היו תווים כלליים בנתיב.
// צור את עוזר הקובץ.
FileHelper fileHelper;
// הגדר את הקובץ שייכתב.
fileHelper.ConfigureFile ("קובץ-helper-example",
FileAggregator::FORMATTED);
// הגדר את התוויות עבור קובץ הפלט המעוצב הזה.
fileHelper.Set2dFormat ("זמן (שניות) = %.3e\tCount = %.0f");
// כתוב את הערכים שנוצרו על ידי הבדיקה. הדרך שאנו
// לספק עוזר לבלבל את מקור העקבות.
fileHelper.WriteProbe ("ns3::Uinteger32Probe",
"/שמות/פולט/מונה",
"תְפוּקָה");
היקף ו מגבלות
נכון לעכשיו, רק בדיקות אלה יושמו וחוברו ל-GnuplotHelper ו
ל-FileHelper:
· BooleanProbe
· DoubleProbe
· Uinteger8Probe
· Uinteger16Probe
· Uinteger32Probe
· TimeProbe
· PacketProbe
· ApplicationPacketProbe
· Ipv4PacketProbe
בדיקות אלו, לפיכך, הן ה-TypeIds היחידות הזמינות לשימוש בהן PlotProbe() ו
WriteProbe().
בחלקים הבאים, אנו מכסים כל אחד מסוגי האובייקטים הבסיסיים (Probe, Collector,
ו-Aggregator) ביתר פירוט, ולהראות כיצד ניתן לחבר אותם יחד באמצעות
API ברמה נמוכה יותר.
בדיקות
סעיף זה מפרט את הפונקציונליות שמספקת מחלקת ה-Probe ל- an ns-3
סימולציה, ונותן דוגמאות כיצד לקודד אותם בתוכנית. הסעיף הזה מיועד ל
משתמשים המעוניינים לפתח סימולציות עם ns-3 כלים ושימוש בנתונים
Collection Framework, שמחלקת Probe היא חלק ממנה, להפקת פלט נתונים איתה
תוצאות הסימולציה שלהם.
בדיקה סקירה כללית
אובייקט Probe אמור להיות מחובר למשתנה מהסימולציה שהערכים שלו
לאורך הניסוי רלוונטיים למשתמש. הגשש יתעד את מה שהיו
ערכים הנלקחים על ידי המשתנה לאורך הסימולציה ומעבירים נתונים כאלה לאחר
חבר במסגרת איסוף הנתונים. אמנם זה מחוץ לתחום של סעיף זה
לדון במה שקורה לאחר שה-Probe מייצר את הפלט שלו, מספיק לומר זאת, על ידי
בסוף הסימולציה, למשתמש יהיה מידע מפורט על הערכים שהיו
מאוחסן בתוך המשתנה הנבדק במהלך הסימולציה.
בדרך כלל, Probe מחובר ל- ns-3 מקור עקבות. באופן זה, בכל פעם שה
מקור המעקב מייצא ערך חדש, ה-Probe צורך את הערך (ומייצא אותו במורד הזרם
לאובייקט אחר דרך מקור עקבות משלו).
ניתן לחשוב על ה-Probe כסוג של מסנן על מקורות עקבות. הסיבות העיקריות ל
חיבור אולי ל-Probe ולא ישירות למקור עקבות הם כדלקמן:
· בדיקות עשויות להיות מופעלות וכיבויות באופן דינמי במהלך הסימולציה עם שיחות אל לְאַפשֵׁר()
ו השבת(). לדוגמה, הפלט של נתונים עשוי להיות מושבת במהלך
שלב חימום סימולציה.
· בדיקות עשויות לבצע פעולות על הנתונים כדי לחלץ ערכים מסובכים יותר
מבנים; למשל, הפלט של ערך גודל החבילה מ-ns3::Packet שהתקבל.
· בדיקות רושמות שם במרחב השמות ns3::Config (באמצעות שמות::הוסף ()) כך שהאחר
חפצים עשויים להתייחס אליהם.
· Probes מספקים שיטה סטטית המאפשרת לבצע מניפולציות של Probe לפי שם, כגון
מה נעשה ב-ns2measure [Cic06]
Stat::put ("my_metric", ID, sample);
המקבילה ל-ns-3 של קוד ns2measure לעיל היא, למשל
DoubleProbe::SetValueByPath ("/path/to/probe", דוגמה);
בריאה
שים לב שלא ניתן ליצור אובייקט של מחלקת בסיס Probe מכיוון שהוא בסיס מופשט
class, כלומר יש לו פונקציות וירטואליות טהורות שלא יושמו. חפץ של
סוג DoubleProbe, שהיא תת מחלקה של מחלקת Probe, תיווצר כאן כדי להראות
מה שצריך להיעשות.
אחד מכריז על DoubleProbe בזיכרון דינמי באמצעות מחלקת המצביע החכם (Ptr ). ל
ליצור DoubleProbe בזיכרון דינמי עם מצביעים חכמים, צריך רק לקרוא ל-
ns-3 שיטה CreateObject ():
Ptr myprobe = CreateObject ();
ההצהרה שלמעלה יוצרת DoubleProbes באמצעות ערכי ברירת המחדל עבור התכונות שלו.
ישנן ארבע תכונות במחלקה DoubleProbe; שניים באובייקט המחלקה הבסיסית
DataCollectionObject, ושניים במחלקת הבסיס של Probe:
· "שם" (DataCollectionObject), StringValue
· "מופעל" (DataCollectionObject), ערך BooleanValue
· "התחל" (בדיקה), ערך זמן
· "עצור" (בדיקה), ערך זמן
אפשר להגדיר תכונות כאלה ביצירת אובייקט באמצעות השיטה הבאה:
Ptr myprobe = CreateObjectWithAttributes (
"שם", StringValue ("myprobe"),
"מופעל", BooleanValue (שקר),
"התחל", TimeValue (שניות (100.0)),
"עצור", TimeValue (שניות (1000.0)));
התחלה ועצירה הם משתני זמן שקובעים את מרווח הפעולה של ה-Probe. ה
בדיקה תוציא נתונים רק אם הזמן הנוכחי של הסימולציה נמצא בתוך זה
הַפסָקָה. ערך הזמן המיוחד של 0 שניות עבור עצור ישבית תכונה זו (כלומר
השאר את ה-Probe מופעל במשך כל הסימולציה). מופעל הוא דגל שמפעיל את ה-Probe או
כבוי, וחייב להיות מוגדר כ-true כדי שה-Probe ייצא נתונים. השם הוא שם האובייקט
במסגרת DCF.
יבוא ו ייצוא נתונים
ns-3 מקורות מעקב מוקלדים היטב, כך שהמנגנונים לחיבור בדיקות לעקבות
מקור ולייצוא נתונים שייכים לתתי המחלקות שלו. למשל, ברירת המחדל
הפצה של ns-3 מספק DoubleProbe בכיתה שנועד להתחבר לעקבות
מקור מייצא ערך כפול. בהמשך נפרט את פעולת ה-DoubleProbe, ו
לאחר מכן דנו כיצד מחלקות Probe אחרות עשויות להיות מוגדרות על ידי המשתמש.
DoubleProbe סקירה כללית
ה-DoubleProbe מתחבר לבעל ערך כפול ns-3 מקור עקבות, ובעצמו מייצא א
שונה בעל ערך כפול ns-3 מקור עקבות.
הקוד הבא, שאוב מ src/stats/examples/double-probe-example.cc, מציג את הבסיס
פעולות של אינסטלציה של ה-DoubleProbe לסימולציה, שבה היא בודקת מונה
מיוצא על ידי אובייקט פולט (מחלקה Emitter).
Ptr emitter = CreateObject ();
שמות::Add ("/שמות/פולט", פולט);
...
Ptr probe1 = CreateObject ();
// חבר את הגשושית למונה של הפולט
bool מחובר = probe1->ConnectByObject ("מונה", פולט);
הקוד הבא בודק את אותו Counter המיוצא על ידי אותו אובייקט פולט. זֶה
עם זאת, DoubleProbe משתמש בנתיב במרחב השמות של התצורה כדי ליצור את
חיבור. שימו לב שהפולט רשם את עצמו במרחב השמות של התצורה לאחר
זה נוצר; אחרת, ה-ConnectByPath לא יעבוד.
Ptr probe2 = CreateObject ();
// הערה, לא מסומן ערך החזרה כאן
probe2->ConnectByPath ("/Names/Emitter/Counter");
ה-DoubleProbe הבא שמוצג להלן יקבע את הערך שלו באמצעות הנתיב שלו פנימה
מרחב השמות של התצורה. שימו לב שהפעם ה-DoubleProbe רשם את עצמו ב-
מרחב השמות של תצורה לאחר יצירתו.
Ptr probe3 = CreateObject ();
probe3->SetName ("StaticallyAccessedProbe");
// עלינו להוסיף אותו למסד הנתונים של התצורה
שמות::Add ("/Names/Probes", probe3->GetName (), probe3);
הפונקציה Count() של הפולט יכולה כעת להגדיר את הערך עבור DoubleProbe זה כ
התעופה
לבטל את
פולט::ספירה (בטל)
{
...
m_counter += 1.0;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
...
}
הדוגמה שלמעלה מראה כיצד הקוד הקורא ל-Probe לא חייב להיות מפורש
התייחסות ל-Probe, אבל יכול לכוון את הגדרת הערך דרך מרחב השמות Config.
זה דומה בפונקציונליות ל- Stat::Put שיטה שהוצגה על ידי נייר ns2measure
[Cic06], ומאפשר למשתמשים להוסיף באופן זמני הצהרות בדיקה כמו הדפס הצהרות
בתוך הקיים ns-3 דגמים. שימו לב שכדי להיות מסוגל להשתמש ב-DoubleProbe בזה
דוגמה כזו, 2 דברים היו נחוצים:
1. קובץ הכותרת של מודול הנתונים הסטטיסטיים נכלל בקובץ ה-cc לדוגמה
2. הדוגמה נעשתה תלויה במודול הנתונים הסטטיסטיים בקובץ ה-wscript שלו.
יש לעשות דברים אנלוגיים על מנת להוסיף Probes אחרים במקומות אחרים ב- ns-3
בסיס קוד.
ניתן להגדיר את הערכים עבור DoubleProbe גם באמצעות הפונקציה DoubleProbe::SetValue(),
בעוד שניתן לקבל את הערכים עבור DoubleProbe באמצעות הפונקציה
DoubleProbe::GetValue().
ה-DoubleProbe מייצא ערכים כפולים במקור העקיבה "פלט" שלו; אובייקט במורד הזרם
יכול לחבר כיור עקבות (NotifyViaProbe) לזה באופן הבא:
מחובר = probe1->TraceConnect ("פלט", probe1->GetName (), MakeCallback (&NotifyViaProbe));
אחר בדיקות
מלבד ה-DoubleProbe, הבדיקות הבאות זמינות גם:
· Uinteger8Probe מתחבר ל- ns-3 מקור מעקב מייצא uint8_t.
· Uinteger16Probe מתחבר ל- ns-3 מקור מעקב מייצא uint16_t.
· Uinteger32Probe מתחבר ל- ns-3 מקור מעקב מייצא uint32_t.
· PacketProbe מתחבר ל- ns-3 מקור מעקב מייצא חבילה.
· ApplicationPacketProbe מתחבר ל- ns-3 מקור מעקב מייצא חבילה ושקע
כתובת.
· Ipv4PacketProbe מתחבר ל- ns-3 מקור מעקב מייצא מנה, אובייקט IPv4 ו
ממשק.
יוצרים חדש בדיקה סוגים
כדי ליצור סוג בדיקה חדש, עליך לבצע את השלבים הבאים:
· ודא שמחלקת ה-Probe החדשה שלך נגזרת ממחלקת הבסיס של ה-Probe.
· ודא שהפונקציות הווירטואליות הטהורות שמחלקת ה-Probe החדשה שלך יורשת מה-
מחלקות בסיס בדיקה מיושמות.
· מצא מחלקת Probe קיימת שמשתמשת במקור עקבות הקרוב ביותר בסוג ל-
סוג מקור העקיבה שה-Probe שלך ישתמש.
· העתק את קובץ ה-header (.h) וקובץ היישום (.cc) של מחלקת Probe הקיימת לשניים
קבצים חדשים עם שמות התואמים את ה-Probe החדש שלך.
· החלף את הסוגים, הארגומנטים והמשתנים בקבצים המועתקים במתאימים
הקלד עבור ה-Probe שלך.
· בצע את השינויים הדרושים כדי לגרום לקוד להדר ולגרום לו להתנהג כפי שהיית מתנהג
כמו.
דוגמאות
שתי דוגמאות יידונו בפירוט כאן:
· דוגמה לבדיקה כפולה
· דוגמה ל-IPv4 Packet Plot
זוגי בדיקה דוגמה
דוגמת הבדיקה הכפולה נדונה בעבר. ניתן למצוא את התוכנית לדוגמה
in src/stats/examples/double-probe-example.cc. לסיכום מה קורה בתוכנית זו,
יש פולט שמייצא מונה שמתגבר לפי תהליך פואסון.
בפרט, מוצגות שתי דרכים לפליטת נתונים:
1. דרך משתנה עקבות המחובר לבדיקה אחת:
TracedValue m_counter; // בדרך כלל זה יהיה סוג מספר שלם
2. דרך מונה שערכו נשלח ל-Probe שני, שמוזכר בשמו ב
מערכת התצורה:
לבטל את
פולט::ספירה (בטל)
{
NS_LOG_FUNCTION (זה);
NS_LOG_DEBUG ("סופר ב" << Simulator::Now ().GetSeconds ());
m_counter += 1.0;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
Simulator::Schedule (שניות (m_var->GetValue ()), &Emitter::Count, this);
}
בואו נסתכל על ה-Probe ביתר שאת. בדיקות יכולות לקבל את הערכים שלהם בכפולה
דרכים:
1. על ידי הגשת ה-Probe ישירות למקור העקבות וחיבור אליו כיור מעקב
2. על ידי ה-Probe גישה למקור המעקב דרך מרחב השמות של התצורה וחיבור א
לשקוע אליו
3. על ידי קוד הקריאה הקורא במפורש ל-Probe's הגדר ערך() שיטה
4. על ידי קוד קורא מפורש קורא SetValueByPath
("/path/through/Config/namespace", ...)
שתי הטכניקות הראשונות צפויות להיות הנפוצות ביותר. גם בדוגמה, ה
חיבור של פונקציית התקשרות חוזרת רגילה מוצג, כפי שנעשה בדרך כלל ב ns-3. זֶה
פונקציית התקשרות חוזרת אינה משויכת לאובייקט Probe. נכנה את המקרה הזה 0) להלן.
// זוהי פונקציה לבדיקת חיבור פונקציה גולמית למקור המעקב
לבטל את
NotifyViaTraceSource (std::string context, double oldVal, double newVal)
{
NS_LOG_DEBUG ("הקשר: " << הקשר << " ישן " << oldVal << " new " << newVal);
}
ראשית, יש להגדיר את הפולט:
Ptr emitter = CreateObject ();
שמות::Add ("/שמות/פולט", פולט);
// האובייקט Emitter אינו משויך לצומת ns-3, אז
// זה לא יתחיל אוטומטית, אז אנחנו צריכים לעשות זאת בעצמנו
סימולטור::לוח זמנים (שניות (0.0), &פולט::התחל, פולט);
ה-DoubleProbes השונים מקיימים אינטראקציה עם הפולט בדוגמה כפי שמוצג להלן.
מקרה 0):
// להלן מראה פונקציונליות טיפוסית ללא בדיקה
// (חבר פונקציית כיור למקור עקבות)
//
מחובר = emitter->TraceConnect ("מונה", "הקשר לדוגמה", MakeCallback (&NotifyViaTraceSource));
NS_ASSERT_MSG (מחובר, "מקור מעקב לא מחובר");
תיק 1):
//
// Probe1 יתחבר ישירות לאובייקט מקור המעקב של Emitter
//
// probe1 יתחבר למקור המעקב של פולט
Ptr probe1 = CreateObject ();
// שם הגשוש יכול לשמש כהקשר שלו במעקב
probe1->SetName ("ObjectProbe");
// חבר את הגשושית למונה של הפולט
מחובר = probe1->ConnectByObject ("מונה", פולט);
NS_ASSERT_MSG (מחובר, "מקור מעקב לא מחובר לבדיקה1");
תיק 2):
//
// Probe2 יתחבר לאובייקט מקור המעקב של Emitter על ידי
// גישה אליו לפי שם נתיב במסד הנתונים של Config
//
// צור בדיקה דומה נוספת; זה יתחבר דרך נתיב Config
Ptr probe2 = CreateObject ();
probe2->SetName ("PathProbe");
// הערה, לא מסומן ערך החזרה כאן
probe2->ConnectByPath ("/Names/Emitter/Counter");
מקרה 4) (מקרה 3 אינו מוצג בדוגמה זו):
//
// Probe3 ייקרא על ידי הפולט ישירות דרך
// שיטה סטטית SetValueByPath().
//
Ptr probe3 = CreateObject ();
probe3->SetName ("StaticallyAccessedProbe");
// עלינו להוסיף אותו למסד הנתונים של התצורה
שמות::Add ("/Names/Probes", probe3->GetName (), probe3);
ולבסוף, הדוגמה מראה כיצד ניתן לחבר את הבדיקות כדי ליצור פלט:
// הבדיקה עצמה אמורה ליצור פלט. ההקשר שאנו מספקים
// לבדיקה זו (במקרה זה, שם הבדיקה) יעזור לבלבל
// מקור העקבות
מחובר = probe3->TraceConnect ("פלט",
"/Names/Probes/StaticallyAccessedProbe/Output",
MakeCallback (&NotifyViaProbe));
NS_ASSERT_MSG (מחובר, "מקור מעקב לא .. מחובר לפלט probe3");
ההתקשרות חזרה הבאה מחוברת ל-Probe בדוגמה זו למטרות המחשה;
בדרך כלל, הגשושית תהיה מחוברת לחפץ אספן.
// זוהי פונקציה לבדיקת חיבורו לפלט הבדיקה
לבטל את
NotifyViaProbe (std::string context, double oldVal, double newVal)
{
NS_LOG_DEBUG ("הקשר: " << הקשר << " ישן " << oldVal << " new " << newVal);
}
IPv4 מנה מגרש דוגמה
דוגמה עלילת מנות IPv4 מבוססת על הדוגמה fifth.cc מה- ns-3 הדרכה. זה
ניתן למצוא src/stats/examples/ipv4-packet-plot-example.cc.
צומת 0 צומת 1
+----------------+ +----------------+
| ns-3 TCP | | ns-3 TCP |
+----------------+ +----------------+
| 10.1.1.1 | | 10.1.1.2 |
+----------------+ +----------------+
| נקודה לנקודה | | נקודה לנקודה |
+----------------+ +----------------+
| |
+----------------------------+
רק נסתכל על ה-Probe, מכיוון שהוא ממחיש ש-Probes עשוי גם לפרוק ערכים ממנו
מבנים (במקרה זה, מנות) ומדווחים על ערכים אלה בתור פלטי מקור מעקב, במקום זאת
מאשר רק לעבור דרך אותו סוג של נתונים.
ישנם היבטים נוספים של דוגמה זו שיוסברו בהמשך התיעוד.
שני סוגי הנתונים המיוצאים הם החבילה עצמה (תְפוּקָה) וספירה של ה
מספר בתים בחבילה (OutputBytes).
TypeId
Ipv4PacketProbe::GetTypeId ()
{
static TypeId tid = TypeId ("ns3::Ipv4PacketProbe")
.SetParent ()
.AddConstructor ()
.AddTraceSource ("פלט",
"החבילה בתוספת אובייקט ה-IPv4 והממשק שלה המשמשים כפלט עבור בדיקה זו",
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_output))
.AddTraceSource ( "OutputBytes",
"מספר הבתים בחבילה",
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_outputBytes))
;
לחזור tid;
}
כאשר כיור המעקב של ה-Probe מקבל חבילה, אם ה-Probe מופעל, הוא יוציא פלט
החבילה שעליה תְפוּקָה מקור מעקב, אבל הוא גם יוציא את מספר הבתים ב-
OutputBytes מקור עקבות.
לבטל את
Ipv4PacketProbe::TraceSink (Ptr חבילה, Ptr ממשק ipv4, uint4_t)
{
NS_LOG_FUNCTION (ממשק << זה << ipv4 <<);
if (IsEnabled ())
{
m_packet = packet;
m_ipv4 = ipv4;
m_interface = ממשק;
m_output (מנות, ipv4, ממשק);
uint32_t packetSizeNew = packet->GetSize ();
m_outputBytes (m_packetSizeOld, packetSizeNew);
m_packetSizeOld = packetSizeNew;
}
}
הפניות
[Cic06]
קלאודיו צ'יקונטי, אנצו מינגוזי, ג'ובאני סטי, "מסגרת משולבת עבור
הפעלת איסוף נתונים וניתוח סטטיסטי יעיל עם ns2, סדנה בנושא
ns-2 (WNS2), פיזה, איטליה, אוקטובר 2006.
אספנים
סעיף זה הוא מציין מיקום לפרט את הפונקציונליות שסופק על ידי האספן
כיתה לא ns-3 סימולציה, ונותן דוגמאות כיצד לקודד אותם בתוכנית.
הערה: נכון ל-ns-3.18, אספנים עדיין בפיתוח ועדיין לא מסופקים כחלק
של המסגרת.
צוברים
סעיף זה מפרט את הפונקציונליות שמספקת מחלקה Aggregator ל- an ns-3
סימולציה. חלק זה מיועד למשתמשים המעוניינים לפתח סימולציות עם
ns-3 כלים ושימוש ב-Data Collection Framework, אשר מחלקה Aggregator היא א
חלק, כדי ליצור פלט נתונים עם תוצאות הסימולציה שלהם.
צבר סקירה כללית
אובייקט Aggregator אמור להיות מחובר למקור עקבות אחד או יותר כדי לעשות זאת
לקבל קלט. אגרגטורים הם נקודת הסיום של הנתונים שנאספו על ידי הרשת של
בדיקות ואספנים במהלך הסימולציה. תפקידו של האגרגטור לקחת את אלה
ערכים והפיכתם לפורמט הפלט הסופי שלהם כגון קבצי טקסט רגיל,
קבצי גיליונות אלקטרוניים, עלילות או מסדי נתונים.
בדרך כלל, אגרגטור מחובר לאספנים אחד או יותר. באופן הזה, בכל פעם
מקורות המעקב של האספנים מייצאים ערכים חדשים, האגרגטור יכול לעבד את הערך כך
שניתן להשתמש בו בפורמט הפלט הסופי שבו ערכי הנתונים יתגוררו לאחר ה-
סימולציה.
שימו לב לדברים הבאים לגבי אגרגטורים:
· ניתן להפעיל ולכבות אגרגטורים באופן דינמי במהלך הסימולציה עם שיחות אל
לְאַפשֵׁר() ו השבת(). לדוגמה, צבירת הנתונים עשויה להיות מושבתת במהלך
שלב החימום של הסימולציה, כלומר הערכים האלה לא ייכללו בגמר
מדיום פלט.
· אגרגטורים מקבלים נתונים מאספנים באמצעות התקשרויות חוזרות. כאשר אספן משויך
ל-Aggregator מתבצעת קריאה ל-TraceConnect כדי לבסס את העקבות של Aggregator
שיטת sink כהתקשרות חוזרת.
עד כה, שני אגרגטורים יושמו:
· GnuplotAggregator
· FileAggregator
GnuplotAggregator
ה-GnuplotAggregator מייצר קבצי פלט המשמשים ליצירת גנופלוטים.
ה-GnuplotAggregator יצור 3 קבצים שונים בסוף הסימולציה:
· קובץ נתוני gnuplot מופרדים מרווחים
· קובץ בקרת גנופלוט
· סקריפט מעטפת ליצירת הגנופלוט
בריאה
אובייקט מסוג GnuplotAggregator ייווצר כאן כדי להראות מה צריך לעשות.
אחד מכריז על GnuplotAggregator בזיכרון דינמי על ידי שימוש במחלקת המצביע החכם
(Ptr ). כדי ליצור GnuplotAggregator בזיכרון דינמי עם מצביעים חכמים, אחד פשוט
צריך להתקשר ל ns-3 שיטה CreateObject (). הקוד הבא מ
src/stats/examples/gnuplot-aggregator-example.cc מראה כיצד לעשות זאת:
string fileNameWithoutExtension = "gnuplot-aggregator";
// צור אגרגטור.
Ptr אגרגטור =
CreateObject (שם קובץWithoutExtension);
הארגומנט הראשון עבור הבנאי, fileNameWithoutExtension, הוא השם של
קבצים הקשורים ל-gnuplot לכתיבה ללא סיומת. GnuplotAggregator זה יצור א
קובץ נתוני gnuplot המופרדים מרווח בשם "gnuplot-aggregator.dat", קובץ בקרת gnuplot
בשם "gnuplot-aggregator.plt", וסקריפט מעטפת ליצירת הגנופלוט בשם +
"gnuplot-aggregator.sh".
הגנופלוט שנוצר יכול לקבל את המפתח שלו ב-4 מיקומים שונים:
· אין מפתח
· מפתח בתוך העלילה (ברירת המחדל)
· מפתח מעל העלילה
· מפתח מתחת לעלילה
ערכי ה-enum של מיקום מפתח הגנופלוט הבאים רשאים לציין את מיקום המפתח:
enum KeyLocation {
אין מפתח,
KEY_INSIDE,
KEY_ABOVE,
KEY_BELOW
};
אם היה רצוי שהמפתח יהיה למטה ולא את מיקום ברירת המחדל של בפנים, אז
תוכל לעשות את הדבר הבא.
aggregator->SetKeyLocation(GnuplotAggregator::KEY_BELOW);
דוגמאות
דוגמה אחת תידון בהרחבה כאן:
· דוגמה ל-Gnuplot Aggregator
גנופלוט צבר דוגמה
ניתן למצוא דוגמה שמתרגלת את GnuplotAggregator
src/stats/examples/gnuplot-aggregator-example.cc.
הגנופלוט הדו-ממדי הבא נוצר באמצעות הדוגמה.
[תמונה] 2-D Gnuplot נוצר על ידי gnuplot-aggregator-example.cc דוגמה..UNINDENT
קוד זה מהדוגמה מראה כיצד לבנות את GnuplotAggregator כפי שנדון
מֵעַל.
void Create2dPlot ()
{
שימוש במרחב השמות std;
string fileNameWithoutExtension = "gnuplot-aggregator";
string plotTitle = "עלילת אגרגטור גנופלוט";
string plotXAxisHeading = "זמן (שניות)";
string plotYAxisHeading = "ערכים כפולים";
string plotDatasetLabel = "ערכי נתונים";
string datasetContext = "מערך נתונים/הקשר/מחרוזת";
// צור אגרגטור.
Ptr אגרגטור =
CreateObject (שם קובץWithoutExtension);
תכונות GnuplotAggregator שונות מוגדרות כולל מערך הנתונים הדו-ממדי שיהיה
זוממה.
// הגדר את המאפיינים של הצבר.
aggregator->SetTerminal ("png");
aggregator->SetTitle (plotTitle);
aggregator->SetLegend (plotXAxisHeading, plotYAxisHeading);
// הוסף מערך נתונים לצבר.
aggregator->Add2dDataset (datasetContext, plotDatasetLabel);
// אגרגטור חייב להיות מופעל
אגרגטור->אפשר ();
לאחר מכן, הערכים הדו-ממדיים מחושבים, וכל אחד מהם נכתב בנפרד ל-
GnuplotAggregator באמצעות Write2d() פונקציה.
זמן כפול;
ערך כפול;
// צור את מערך הנתונים הדו-ממדי.
עבור (זמן = -5.0; זמן <= +5.0; זמן += 1.0)
{
// חשב את העקומה הדו-ממדית
//
// 2
// ערך = זמן .
//
ערך = זמן * זמן;
// הוסף את הנקודה הזו לעלילה.
aggregator->Write2d (datasetContext, זמן, ערך);
}
// השבת רישום נתונים עבור האגרגטור.
אגרגטור->השבת ();
}
File Aggregator
ה-FileAggregator שולח את הערכים שהוא מקבל לקובץ.
ה-FileAggregator יכול ליצור 4 סוגים שונים של קבצים:
· מעוצב
· רווח מופרד (ברירת המחדל)
· מופרד באמצעות פסיקים
· כרטיסיות מופרדות
קבצים מעוצבים משתמשים במחרוזות פורמט בסגנון C ובפונקציה sprintf() כדי להדפיס אותם
ערכים בקובץ הנכתב.
בריאה
אובייקט מסוג FileAggregator ייווצר כאן כדי להראות מה צריך לעשות.
אחד מכריז על FileAggregator בזיכרון דינמי באמצעות מחלקת המצביע החכם (Ptr ).
כדי ליצור FileAggregator בזיכרון דינמי עם מצביעים חכמים, צריך רק להתקשר
מה היא ns-3 שיטה CreateObject. הקוד הבא מ
src/stats/examples/file-aggregator-example.cc מראה כיצד לעשות זאת:
string fileName = "file-aggregator-formatted-values.txt";
// צור אגרגטור שיהיו לו ערכים מעוצבים.
Ptr אגרגטור =
CreateObject (שם קובץ, FileAggregator::FORMATTED);
הארגומנט הראשון של הבנאי, שם הקובץ, הוא שם הקובץ לכתיבה; ה
ארגומנט שני, fileType, הוא סוג הקובץ לכתיבה. FileAggregator זה יצור א
קובץ בשם "file-aggregator-formatted-values.txt" עם הערכים שלו מודפסים כפי שצוין על ידי
fileType, כלומר, מעוצב במקרה זה.
ערכי ה-enum של סוג הקובץ הבאים מותרים:
enum FileType {
מעוצב,
SPACE_SEPARATED,
מופרד באמצעות פסיקים,
TAB_SPARATED
};
דוגמאות
דוגמה אחת תידון בהרחבה כאן:
· דוגמה לקובץ אגרגטור
שלח צבר דוגמה
דוגמה להפעלת FileAggregator ניתן למצוא ב
src/stats/examples/file-aggregator-example.cc.
קובץ הטקסט הבא עם 2 עמודות של ערכים מופרדות בפסיקים נוצר באמצעות ה-
דוגמא.
-5,25
-4,16
-3,9
-2,4
-1,1
0,0
1,1
2,4
3,9
4,16
5,25
קוד זה מהדוגמה מראה כיצד לבנות את FileAggregator כפי שנדון
מֵעַל.
void CreateCommaSeparatedFile ()
{
שימוש במרחב השמות std;
string fileName = "file-aggregator-comma-separated.txt";
string datasetContext = "מערך נתונים/הקשר/מחרוזת";
// צור אגרגטור.
Ptr אגרגטור =
CreateObject (שם קובץ, FileAggregator::COMMA_SEPARATED);
תכונות FileAggregator מוגדרות.
// אגרגטור חייב להיות מופעל
אגרגטור->אפשר ();
לאחר מכן, הערכים הדו-ממדיים מחושבים, וכל אחד מהם נכתב בנפרד ל-
FileAggregator באמצעות ה Write2d() פונקציה.
זמן כפול;
ערך כפול;
// צור את מערך הנתונים הדו-ממדי.
עבור (זמן = -5.0; זמן <= +5.0; זמן += 1.0)
{
// חשב את העקומה הדו-ממדית
//
// 2
// ערך = זמן .
//
ערך = זמן * זמן;
// הוסף את הנקודה הזו לעלילה.
aggregator->Write2d (datasetContext, זמן, ערך);
}
// השבת רישום נתונים עבור האגרגטור.
אגרגטור->השבת ();
}
קובץ הטקסט הבא עם 2 עמודות של ערכים מעוצבים נוצר גם באמצעות ה-
דוגמא.
זמן = -5.000e+00 ערך = 25
זמן = -4.000e+00 ערך = 16
זמן = -3.000e+00 ערך = 9
זמן = -2.000e+00 ערך = 4
זמן = -1.000e+00 ערך = 1
זמן = 0.000e+00 ערך = 0
זמן = 1.000e+00 ערך = 1
זמן = 2.000e+00 ערך = 4
זמן = 3.000e+00 ערך = 9
זמן = 4.000e+00 ערך = 16
זמן = 5.000e+00 ערך = 25
קוד זה מהדוגמה מראה כיצד לבנות את FileAggregator כפי שנדון
מֵעַל.
void CreateFormattedFile ()
{
שימוש במרחב השמות std;
string fileName = "file-aggregator-formatted-values.txt";
string datasetContext = "מערך נתונים/הקשר/מחרוזת";
// צור אגרגטור שיהיו לו ערכים מעוצבים.
Ptr אגרגטור =
CreateObject (שם קובץ, FileAggregator::FORMATTED);
תכונות FileAggregator מוגדרות, כולל מחרוזת הפורמט בסגנון C לשימוש.
// הגדר את הפורמט של הערכים.
aggregator->Set2dFormat ("זמן = %.3e\tValue = %.0f");
// אגרגטור חייב להיות מופעל
אגרגטור->אפשר ();
לאחר מכן, הערכים הדו-ממדיים מחושבים, וכל אחד מהם נכתב בנפרד ל-
FileAggregator באמצעות ה Write2d() פונקציה.
זמן כפול;
ערך כפול;
// צור את מערך הנתונים הדו-ממדי.
עבור (זמן = -5.0; זמן <= +5.0; זמן += 1.0)
{
// חשב את העקומה הדו-ממדית
//
// 2
// ערך = זמן .
//
ערך = זמן * זמן;
// הוסף את הנקודה הזו לעלילה.
aggregator->Write2d (datasetContext, זמן, ערך);
}
// השבת רישום נתונים עבור האגרגטור.
אגרגטור->השבת ();
}
מתאמים
סעיף זה מפרט את הפונקציונליות שמספקת מחלקת המתאם ל- an ns-3
סימולציה. חלק זה מיועד למשתמשים המעוניינים לפתח סימולציות עם
ns-3 כלים ושימוש במסגרת איסוף הנתונים, שמחלקת המתאם היא חלק ממנה,
ליצור פלט נתונים עם תוצאות הסימולציה שלהם.
הערה: המונח 'מתאם' עשוי להיות מאויית גם 'מתאם'; בחרנו באיות מיושר
עם תקן C++.
מתאם סקירה כללית
מתאם משמש ליצירת חיבורים בין סוגים שונים של אובייקטי DCF.
עד כה, מתאם אחד יושם:
· TimeSeriesAdaptor
זְמַן סדרה מתאם
ה-TimeSeriesAdaptor מאפשר ל-Probes להתחבר ישירות ל-Aggregators ללא צורך
אספן בין לבין.
שני עוזרי DCF המיושמים משתמשים ב-TimeSeriesAdaptors כדי לבצע בדיקה
ערכים מסוגים שונים ופלט את הזמן הנוכחי בתוספת הערך עם שניהם מומרים
לכפילים.
תפקידה של מחלקת TimeSeriesAdaptor הוא של מתאם, שלוקח ערך גולמי
בדיקה של נתוני בדיקה מסוגים שונים ומוציאה טופלה של שני ערכים כפולים. הראשון הוא א
חותמת זמן, אשר עשויה להיות מוגדרת ברזולוציות שונות (למשל שניות, אלפיות שניות וכו') ב
העתיד אך מקודד כעת ל-Seconds. השני הוא ההמרה של א
ערך לא כפול לערך כפול (אפשר עם אובדן דיוק).
היקף/הגבלות
סעיף זה דן בהיקף ובמגבלות של מסגרת איסוף הנתונים.
נכון לעכשיו, רק בדיקות אלה יושמו ב-DCF:
· BooleanProbe
· DoubleProbe
· Uinteger8Probe
· Uinteger16Probe
· Uinteger32Probe
· TimeProbe
· PacketProbe
· ApplicationPacketProbe
· Ipv4PacketProbe
נכון לעכשיו, אין אספנים זמינים ב-DCF, אם כי BasicStatsCollector נמצא תחת
פיתוח.
נכון לעכשיו, רק אגרגטורים אלה יושמו ב-DCF:
· GnuplotAggregator
· FileAggregator
נכון לעכשיו, רק המתאם הזה יושם ב-DCF:
מתאם סדרת זמן.
שלנו תיק עבודות
חלק זה דן בעבודה העתידית שתעשה על מסגרת איסוף הנתונים.
הנה כמה דברים שעדיין צריך לעשות:
· חבר מקורות עקבות נוספים ns-3 קוד כדי להוציא יותר ערכים מהסימולטור.
· ליישם יותר סוגים של Probes ממה שיש כיום.
· יישם יותר מסתם אספן הדו-ממד הנוכחי היחיד, BasicStatsCollector.
· הטמעת אגרגטורים נוספים.
· הטמע יותר מסתם מתאמים.
סטטיסטי מסגרת
פרק זה מתאר את העבודה על איסוף נתוני סימולציה והמסגרת הסטטיסטית עבור
ns-3.
קוד המקור של המסגרת הסטטיסטית נמצא בספרייה src/stats.
שערים
המטרות העיקריות למאמץ זה הן הבאות:
· לספק פונקציונליות להקלטה, לחשב ולהציג נתונים וסטטיסטיקות לניתוח
של סימולציות רשת.
· שפר את ביצועי הסימולציה על ידי הפחתת הצורך ביצירת כניסות מעקב נרחבות
על מנת לאסוף נתונים.
· אפשר בקרת סימולציה באמצעות סטטיסטיקה מקוונת, למשל סיום סימולציות או
ניסויים חוזרים.
יעדי משנה נגזרים ותכונות יעד אחרות כוללות את הדברים הבאים:
· אינטגרציה עם מערכת המעקב הקיימת ns-3 כמסגרת המכשור הבסיסית
של מנוע הסימולציה הפנימי, למשל ערימות רשת, התקני רשת וערוצים.
· מתן אפשרות למשתמשים להשתמש במסגרת הסטטיסטיקה ללא צורך בשימוש במעקב
מערכת.
· עזרה למשתמשים ליצור, לצבור ולנתח נתונים על פני מספר ניסויים.
· תמיכה במכשור שנוצר על ידי המשתמש, למשל של אירועים ספציפיים ליישום ו
אמצעים.
· זיכרון נמוך ותקורה של CPU כאשר החבילה אינה בשימוש.
· מינוף כלי ניתוח ופלט קיימים ככל האפשר. המסגרת עשויה
לספק כמה נתונים סטטיסטיים בסיסיים, אבל ההתמקדות היא באיסוף נתונים והכנתם
נגיש למניפולציה בכלים מבוססים.
· תמיכה בסופו של דבר בהפצת רפליקציות עצמאיות חשובה אך אינה כלולה
בסבב התכונות הראשון.
סקירה כללית
מסגרת הסטטיסטיקה כוללת את התכונות הבאות:
· מסגרת הליבה ושני אוספי נתונים בסיסיים: מונה, ו-min/max/avg/total
מַשׁקִיף.
· הרחבות של אלה לעבוד בקלות עם זמנים ומנות.
· פלט טקסט רגיל מעוצב עבור OMNet++.
· פלט מסד נתונים באמצעות SQLite, מנוע SQL עצמאי, קל משקל וביצועים גבוהים.
· מטא נתונים חובה ופתוחים לתיאור ועבודה עם ריצות.
· דוגמה המבוססת על הניסוי הרעיוני של בחינת המאפיינים של NS-3
ביצועי WiFi אד-הוק כברירת מחדל. הוא משלב את הדברים הבאים:
· בונה של רשת WiFi אד-הוק של שני צמתים, כשהצמתים במרחק פרמטרי
מלבד.
· יישומי מקור ותעבורה UDP עם התנהגות שונה במקצת
ווי מדידה מאשר מחלקות המניות.
· איסוף נתונים מליבת NS-3 באמצעות אותות מעקב קיימים, בפרט נתונים על
מסגרות משודרות ומתקבלות על ידי אובייקטי WiFi MAC.
· מכשור של יישומים מותאמים אישית על ידי חיבור אותות מעקב חדשים לסטט
מסגרת, כמו גם באמצעות עדכונים ישירים. מידע נרשם על סך כל החבילות
נשלח והתקבל, בתים ששודרו ועיכוב מקצה לקצה.
· דוגמה לשימוש בתגי מנות למעקב אחר עיכוב מקצה לקצה.
· סקריפט בקרה פשוט המריץ מספר ניסויים של הניסוי במשתנה
מרחיק ומבקש את מסד הנתונים המתקבל כדי לייצר גרף באמצעות GNUPlot.
לעשות
פריטים בעדיפות גבוהה כוללים:
· הכללת קוד סטטיסטיקה מקוון, למשל עבור מרווחי ביטחון יעילים בזיכרון.
· הוראות באספי הנתונים לסיום ריצות, כלומר כאשר סף או
הביטחון מתקיים.
· אוספי נתונים לרישום דגימות לאורך זמן, ופלט לפורמטים השונים.
· הדגימו כתיבת דבק אירועים מחזורי פשוט כדי לסקור באופן קבוע ערך כלשהו.
כל אחד מאלה צריך להיות פשוט לשילוב במסגרת הנוכחית.
גישה
המסגרת מבוססת על עקרונות הליבה הבאים:
· ניסוי ניסוי אחד מתבצע על ידי מופע אחד של תוכנית סימולציה, בין אם ב
במקביל או סדרתי.
· סקריפט בקרה מבצע מופעים של הסימולציה, משתנים פרמטרים לפי הצורך.
· נתונים נאספים ומאוחסנים לצורך תכנון וניתוח באמצעות סקריפטים חיצוניים ו
כלים קיימים.
· אמצעים בתוך ליבת ns-3 ננקטים על ידי חיבור ה-stat framework לקיים
אותות מעקב.
· ניתן להשתמש באותות מעקב או מניפולציה ישירה של המסגרת למכשיר מותאם אישית
קוד סימולציה.
אותם מרכיבים בסיסיים של המסגרת והאינטראקציות ביניהם מתוארים ב-
הדמות הבאה. [תמונה]
דוגמה
חלק זה עובר תהליך של בניית ניסוי במסגרת ו
הפקת נתונים לניתוח (גרפים) ממנו, הדגמת המבנה וה-API לאורך
הדרך.
שאלה
''מהם הביצועים (המדומים) של ה-WiFi NetDevices של ns-3 (באמצעות ברירת המחדל
הגדרות)? כמה רחוקים יכולים להיות צמתים אלחוטיים בסימולציה לפני שהם לא יכולים
לתקשר בצורה מהימנה?''
· השערה: בהתבסס על ידע על ביצועים בחיים האמיתיים, הצמתים צריכים לתקשר
סביר עד לפחות 100 מטר זה מזה. תקשורת מעבר ל-200 מטר לא צריכה להיות
אפשרי.
למרות שאלה לא נפוצה מאוד בהקשרי סימולציה, מדובר בנכס חשוב
אשר מפתחי סימולציה צריכים להיות בעלי הבנה בסיסית. זה גם נפוץ
מחקר שנעשה על חומרה חיה.
הדמיה תָכְנִית
הדבר הראשון שצריך לעשות ביישום הניסוי הזה הוא פיתוח הסימולציה
תכנית. את הקוד עבור דוגמה זו ניתן למצוא ב examples/stats/wifi-example-sim.cc.
זה עושה את השלבים העיקריים הבאים.
· הצהרת פרמטרים וניתוח שורת הפקודה באמצעות ns3::CommandLine.
מרחק כפול = 50.0;
פורמט מחרוזת ("OMNet++");
ניסוי מחרוזת ("מבחן wifi-distance");
אסטרטגיית מחרוזת ("ברירת המחדל של wifi");
string runID;
CommandLine cmd;
cmd.AddValue("distance", "מרחק זה מזה למיקום צמתים (במטרים).", distance);
cmd.AddValue("פורמט", "פורמט לשימוש עבור פלט נתונים.", פורמט);
cmd.AddValue("ניסוי", "מזהה לניסוי.", ניסוי);
cmd.AddValue("אסטרטגיה", "מזהה לאסטרטגיה.", אסטרטגיה);
cmd.AddValue("run", "מזהה לריצה.", runID);
cmd.Parse (argc, argv);
· יצירת צמתים וערימות רשת באמצעות ns3::NodeContainer, ns3::WiFiHelper, ו
ns3::InternetStackHelper.
צמתים של NodeContainer;
צמתים.צור(2);
WifiHelper wifi;
wifi.SetMac("ns3::AdhocWifiMac");
wifi.SetPhy("ns3::WifiPhy");
NetDeviceContainer nodeDevices = wifi.Install(nodes);
InternetStackHelper אינטרנט;
internet.Install(צמתים);
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase("192.168.0.0", "255.255.255.0");
ipAddrs.Assign(nodeDevices);
· מיקום הצמתים באמצעות ns3::MobilityHelper. כברירת מחדל לצמתים יש סטטי
ניידות ולא יזוז, אבל חייב להיות ממוקם במרחק הנתון זה מזה. יש
מספר דרכים לעשות זאת; זה נעשה כאן באמצעות ns3::ListPositionAllocator, אשר מצייר
עמדות מתוך רשימה נתונה.
MobilityHelper ניידות;
Ptr positionAlloc =
CreateObject ();
positionAlloc->Add(Vector(0.0, 0.0, 0.0));
positionAlloc->Add(Vector(0.0, מרחק, 0.0));
mobility.SetPositionAllocator(positionAlloc);
mobility.Install(צמתים);
· התקנת מחולל תנועה וכיור תנועה. המנייה יישומים יכול להיות
בשימוש, אבל הדוגמה כוללת אובייקטים מותאמים אישית ב src/test/test02-apps.(cc|h). אלה
יש התנהגות פשוטה, יצירת מספר נתון של מנות מרווחות במרווח נתון.
מכיוון שיש רק אחד מכל אחד, הם מותקנים באופן ידני; עבור סט גדול יותר
ns3::ApplicationHelper ניתן להשתמש בכיתה. ההערות החוצה Config::Set שינויים בקו
היעד של החבילות, מוגדר לשידור כברירת מחדל בדוגמה זו. ציין זאת
באופן כללי ל-WiFi עשוי להיות ביצועים שונים עבור מסגרות שידור ו-unicast עקב
מדיניות שונה של בקרת קצב ושידור חוזר של MAC.
Ptr appSource = NodeList::GetNode(0);
Ptr שולח = CreateObject ();
appSource->AddApplication(שולח);
שולח->התחל(שניות(1));
Ptr appSink = NodeList::GetNode(1);
Ptr receiver = CreateObject ();
appSink->AddApplication(מקלט);
מקלט->התחל(שניות(0));
// Config::Set("/NodeList/*/ApplicationList/*/$Sender/Destination",
// Ipv4AddressValue("192.168.0.2"));
· קביעת תצורת הנתונים והסטטיסטיקות שייאסוף. הפרדיגמה הבסיסית היא שא
ns3::DataCollector האובייקט נוצר כדי להחזיק מידע על הריצה הספציפית הזו, to
אילו צופים ומחשבונים מחוברים כדי ליצור נתונים בפועל. חשוב,
מידע הפעלה כולל תוויות עבור ''ניסוי'', ''אסטרטגיה'', ''קלט'' ו-
''לָרוּץ''. אלה משמשים מאוחר יותר לזהות ולקבץ בקלות נתונים מניסויים מרובים.
· הניסוי הוא המחקר שהניסוי הזה חבר בו. הנה זה ב-WiFi
ביצועים ומרחק.
· האסטרטגיה היא הקוד או הפרמטרים הנבדקים בניסוי זה. בדוגמה זו
זה קבוע, אבל הרחבה ברורה תהיה לחקור קצת WiFi שונה
תעריפים, שכל אחד מהם יהיה אסטרטגיה שונה.
· הקלט הוא הבעיה המסוימת שניתנה לניסוי זה. כאן זה פשוט ה
מרחק בין שני הצמתים.
· ה-runID הוא מזהה ייחודי עבור ניסוי זה שבו המידע שלו מתויג
לזיהוי בניתוח מאוחר יותר. אם לא ניתן מזהה ריצה, התוכנית לדוגמה עושה
מזהה ריצה (חלש) תוך שימוש בזמן הנוכחי.
ארבע פיסות המטא נתונים הללו נדרשות, אך ייתכן שתרצה יותר. הם עשויים להתווסף
לרשומה באמצעות ה ns3::DataCollector::AddMetadata() שִׁיטָה.
נתונים של DataCollector;
data.DescribeRun(ניסוי, אסטרטגיה, קלט, runID);
data.AddMetadata("מחבר", "tjkopena");
תצפית וחישוב בפועל נעשים על ידי ns3::DataCalculator חפצים, מתוכם
קיימים מספר סוגים שונים. אלה נוצרים על ידי תוכנית הסימולציה, המצורפת ל
קוד דיווח או דגימה, ולאחר מכן נרשם ב- ns3::DataCollector אז הם יעשו זאת
יש לשאול מאוחר יותר עבור הפלט שלהם. מנגנון תצפית קל אחד הוא להשתמש בקיים
להתחקות אחר מקורות, למשל למכשיר אובייקטים בליבת ns-3 מבלי לשנות אותם
קוד. כאן מונה מחובר ישירות לאות מעקב בשכבת WiFi MAC על
צומת המטרה.
Ptr totalRx = CreateObject ();
totalRx->SetKey("wifi-rx-frames");
Config::Connect("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Rx",
MakeCallback(&PacketCounterCalculator::FrameUpdate, totalRx));
data.AddDataCalculator(totalRx);
ניתן גם לבצע מניפולציות ישירות במחשבונים. בדוגמה זו נוצר מונה ו
הועבר לאפליקציית כיור התנועה כדי להתעדכן כאשר מנות מתקבלות.
Ptr > appRx = CreateObject >();
appRx->SetKey("receiver-rx-packets");
מקלט->SetCounter(appRx);
data.AddDataCalculator(appRx);
כדי להגדיל את הספירה, קוד עיבוד החבילות של הכיור קורא לאחד מה-
שיטות העדכון של המחשבון.
m_calc->עדכון();
התוכנית כוללת גם כמה דוגמאות אחרות, תוך שימוש בשתיהן הפרימיטיביות
מחשבונים כגון ns3::CounterCalculator וכאלה המותאמים לצפייה במנות ו
פִּי. ב src/test/test02-apps.(cc|h) זה גם יוצר תג מותאם אישית פשוט שבו הוא משתמש
כדי לעקוב אחר עיכוב מקצה לקצה עבור מנות שנוצרו, דיווח על תוצאות לא
ns3::TimeMinMaxAvgTotalCalculator מחשבון נתונים.
· הפעלת הסימולציה, שהיא מאוד פשוטה ברגע שנבנה.
סימולטור::Run();
· יצירת או OMNet++ or SQLite פלט, בהתאם לארגומנטים של שורת הפקודה. ל
לעשות את זה א ns3::DataOutputInterface האובייקט נוצר ומוגדר. הסוג הספציפי
זה יקבע את פורמט הפלט. לאחר מכן ניתן לאובייקט זה את
ns3::DataCollector אובייקט שהוא חוקר כדי להפיק את הפלט.
Ptr תְפוּקָה;
if (פורמט == "OMNet++") {
NS_LOG_INFO("יצירת פלט נתונים בפורמט OMNet++.");
output = CreateObject ();
אחר} {
# ifdef STAT_USE_DB
NS_LOG_INFO("יצירת פלט נתונים בפורמט SQLite.");
output = CreateObject ();
# אנדיף
}
פלט->פלט(נתונים);
· שחרור כל זיכרון המשמש את הסימולציה. זה צריך לבוא בסוף הראשית
פונקציה עבור הדוגמה.
סימולטור::Destroy();
רישום
כדי לראות מה התוכנית לדוגמה, היישומים והמסגרת הסטטיסטית עושים בפירוט, הגדר
מה היא NS_LOG משתנה כראוי. הדברים הבאים יספקו פלט רב מכל
שלוש.
$ export NS_LOG=WiFiDistanceExperiment:WiFiDistanceApps
שימו לב שזה מאט את הסימולציה בצורה יוצאת דופן.
מדגם תְפוּקָה
קומפילציה והפעלה פשוטה של תוכנית הבדיקה תצורף OMNet++ פלט מעוצב כגון
הבא ל data.sca.
הרץ run-1212239121
ניסוי attr "מבחן מרחק wifi"
אסטרטגיית attr "ברירת המחדל של wifi"
קלט attr "50"
תיאור attr ""
attr "author" "tjkopena"
מסגרות wifi-tx-מסגרות סקלריות סופרות 30
מסגרות wifi-rx-מסגרות סקלריות סופרות 30
Sender-tx-packets סקלארי סופר 30
מקלט-rx-packets סקלארי סופר 30
scalar tx-pkt-size count 30
scalar tx-pkt-size סה"כ 1920
ממוצע tx-pkt-scalar 64
scalar tx-pkt-size max 64
scalar tx-pkt-size min 64
ספירת עיכובים סקלרים 30
השהיה סקלרית בסך הכל 5884980ns
עיכוב סקלרי ממוצע 196166ns
השהיה סקלרית מקסימום 196166ns
עיכוב סקלרי מינימום 196166ns
שליטה תסריט
על מנת להפוך את איסוף הנתונים לאוטומטי במגוון כניסות (מרחקים), Bash פשוט
סקריפט משמש לביצוע סדרה של סימולציות. ניתן למצוא אותו ב
examples/stats/wifi-example-db.sh. התסריט נועד להיות מופעל מה- דוגמאות/סטטיסטיקות/
במדריך.
התסריט עובר דרך קבוצה של מרחקים, אוסף את התוצאות ל- SQLite
מאגר מידע. בכל מרחק נערכים חמישה ניסויים כדי לתת תמונה טובה יותר של הצפוי
ביצועים. הניסוי כולו לוקח רק כמה עשרות שניות לפעול בקצה נמוך
מכונה מכיוון שאין פלט במהלך הסימולציה ונוצר תעבורה מועטה.
#!/ Bin / sh
DISTANCES="25 50 75 100 125 145 147 150 152 155 157 160 162 165 167 170 172 175 177 180"
TRIALS="1 2 3 4 5"
echo WiFi Experiment Experiment
if [-e data.db]
אז
echo Kill data.db?
קרא ANS
if [ "$ANS" = "yes" -o "$ANS" = "y" ]
אז
echo מחיקת מסד נתונים
rm data.db
fi
fi
לניסיון ב-$TRIALS
do
למרחק ב-$DISTANCES
do
echo Trial $trial, מרחק $distance
./bin/test02 --format=db --distance=$distance --run=run-$distance-$trial
עשה
עשה
אָנָלִיזָה ו סיכום
לאחר שכל הניסויים נערכו, הסקריפט מבצע שאילתת SQL פשוטה על
מסד נתונים באמצעות SQLite תוכנית שורת הפקודה. השאילתה מחשבת אובדן מנות ממוצע ב
כל סט של ניסויים הקשורים לכל מרחק. זה לא לוקח בחשבון שונה
אסטרטגיות, אבל המידע קיים במסד הנתונים כדי ליצור כמה הרחבות פשוטות
ולעשות זאת. לאחר מכן, הנתונים שנאספו מועברים ל-GNUPlot לצורך ציור גרפים.
CMD="select exp.input,avg(100-((rx.value*100)/tx.value)) \
מ-Singletons rx, Singletons tx, Experiments exp \
כאשר rx.run = tx.run AND \
rx.run = exp.run AND \
rx.name='receiver-rx-packets' ו-\
tx.name='sender-tx-packets' \
קבץ לפי exp.input \
סדר לפי abs(exp.input) ASC;"
sqlite3 -noheader data.db "$CMD" > wifi-default.data
sed -i "s/|/ /" wifi-default.data
gnuplot wifi-example.gnuplot
סקריפט GNUPlot נמצא ב examples/stats/wifi-example.gnuplot פשוט מגדיר את הפלט
פורמט וכמה עיצוב בסיסי עבור הגרף.
הגדר דיוקן לאחר כתב מסוף משופר lw 2 "Helvetica" 14
גודל סט 1.0, 0.66
#-------------------------------------------------------- ------
הגדר "wifi-default.eps"
#set title "אובדן חבילה על פני מרחק"
הגדר xlabel "מרחק (מ') --- ממוצע של 5 ניסויים לכל נקודה"
הגדר טווח xrange [0:200]
הגדר את התווית "% אובדן מנות"
הגדר yrange [0:110]
עלילה "wifi-default.data" עם כותרת השורות "WiFi Defaults"
סוֹף תוֹצָאָה
הגרף המתקבל אינו מספק הוכחה לכך שביצועי ברירת המחדל של מודל ה-WiFi הם
בלתי הגיוני בהכרח ונותן קצת ביטחון לנאמנות סמלית לפחות ל
מְצִיאוּת. חשוב מכך, החקירה הפשוטה הזו בוצעה לאורך כל הדרך
באמצעות המסגרת הסטטיסטית. הַצלָחָה! [תמונה]
זמן אמת
ns-3 תוכנן לשילוב בסביבות מבחן ומכונות וירטואליות. ל
לשלב עם ערימות רשת אמיתיות ולפלוט/לצרוך מנות, מתזמן בזמן אמת הוא
צריך לנסות לנעול את שעון הסימולציה עם שעון החומרה. אנו מתארים כאן א
מרכיב זה: מתזמן RealTime.
מטרת המתזמן בזמן אמת היא לגרום להתקדמות שעון הסימולציה
להתרחש באופן סינכרוני ביחס לבסיס זמן חיצוני כלשהו. ללא נוכחות של
בסיס זמן חיצוני (שעון קיר), זמן הסימולציה קופץ באופן מיידי מאחד המדומים
זמן עד הבא.
התנהגות
בעת שימוש במתזמן שאינו בזמן אמת (ברירת המחדל ב ns-3), הסימולטור מקדם את
זמן סימולציה לאירוע המתוכנן הבא. במהלך ביצוע האירוע, זמן הסימולציה הוא
קָפוּא. עם מתזמן זמן אמת, ההתנהגות דומה מנקודת המבט של
מודלים של סימולציה (כלומר, זמן הסימולציה מוקפא במהלך ביצוע האירוע), אך ביניהם
אירועים, הסימולטור ינסה לשמור את שעון הסימולציה מיושר עם המכונה
שָׁעוֹן.
כאשר אירוע מסתיים ביצוע, והמתזמן עובר לאירוע הבא, ה-
מתזמן משווה את זמן ביצוע האירוע הבא עם שעון המכונה. אם הבא
האירוע מתוכנן לזמן עתידי, הסימולטור ישן עד לזמן אמת זה
ולאחר מכן מבצע את האירוע הבא.
יכול לקרות שבגלל העיבוד הגלום בביצוע אירועי סימולציה,
שהסימולטור לא יכול לעמוד בקצב בזמן אמת. במקרה כזה, זה תלוי במשתמש
תצורה מה לעשות. יש שני ns-3 תכונות השולטות בהתנהגות. ה
הראשון הוא ns3::RealTimeSimulatorImpl::SynchronizationMode. שני הערכים האפשריים עבור
תכונה זו הם BestEffort (ברירת המחדל) או HardLimit. במצב "BestEffort", ה
סימולטור פשוט ינסה להתעדכן בזמן אמת על ידי ביצוע אירועים עד שיגיע ל-a
נקודה שבה האירוע הבא יהיה בעתיד (בזמן אמת), אחרת הסימולציה מסתיימת. ב
במצב BestEffort, אם כן, ייתכן שהסימולציה תצרוך יותר זמן מאשר
זמן שעון קיר. האפשרות האחרת "HardLimit" תגרום לביטול הסימולציה אם
חריגה מסף הסובלנות. תכונה זו היא ns3::RealTimeSimulatorImpl::HardLimit
וברירת המחדל היא 0.1 שניות.
אופן פעולה שונה הוא מצב שבו זמן מדומה לֹא קפוא במהלך אירוע
ביצוע. מצב זה של סימולציה בזמן אמת יושם אך הוסר מה- ns-3 עץ
בגלל שאלות אם זה יהיה שימושי. אם משתמשים מעוניינים בזמן אמת
סימולטור שזמן הסימולציה עבורו לא קופא במהלך ביצוע האירוע (כלומר, כל
לקרוא ל סימולטור::Now() מחזירה את זמן שעון הקיר הנוכחי, לא את השעה שבה
האירוע התחיל בביצוע), אנא צור קשר עם רשימת התפוצה של ns-developers.
נוֹהָג
השימוש בסימולטור בזמן אמת הוא פשוט, מנקודת מבט של סקריפטים.
המשתמשים רק צריכים להגדיר את התכונה SimulatorImplementationType לזמן אמת
סימולטור, כגון:
GlobalValue::Bind ("SimulatorImplementationType",
StringValue ("ns3::RealtimeSimulatorImpl"));
יש תסריט בפנים examples/realtime/realtime-udp-echo.cc יש לזה דוגמה איך לעשות
להגדיר את ההתנהגות בזמן אמת. לְנַסוֹת:
$ ./waf --הפעל realtime-udp-echo
אם הסימולטור יעבוד במיטב המאמץ או במדיניות מגבלה קשה, נקבע
לפי התכונות שהוסברו בסעיף הקודם.
יישום
היישום כלול בקבצים הבאים:
· src/core/model/realtime-simulator-impl.{cc,h}
· src/core/model/wall-clock-synchronizer.{cc,h}
על מנת ליצור מתזמן בזמן אמת, לקירוב ראשון אתה רק רוצה לגרום
זמן הסימולציה קופץ כדי לצרוך זמן אמת. אנו מציעים לעשות זאת באמצעות שילוב של
שינה ועסוקה מחכה. המתנה-שינה גורמת לתהליך ההתקשרות (חוט) להניב את
מעבד למשך זמן מסוים. למרות שניתן להעביר את פרק הזמן המצוין הזה
לרזולוציה של ננו-שניות, הוא למעשה מומר לפירוט ספציפי למערכת ההפעלה. ב
לינוקס, הפירוט נקרא Jiffy. בדרך כלל רזולוציה זו אינה מספקת עבור
הצרכים שלנו (בסדר גודל של עשר אלפיות שניות), אז אנחנו מתעגלים למטה וישנים קצת
מספר קטן יותר של Jiffies. לאחר מכן התהליך מתעורר לאחר המספר שצוין של
Jiffies עבר. בשלב זה, יש לנו עוד זמן לחכות. הזמן הזה הוא
בדרך כלל קטן יותר מזמן השינה המינימלי, אז אנחנו עסוקים-מחכים לשארית
זְמַן. זה אומר שהחוט פשוט יושב במחזוריות לצרוך לולאה עד ל
הזמן הרצוי מגיע. לאחר השילוב של המתנה-שינה ועסוקה, הזמן האמיתי שחלף
שעון (קיר) צריך להתאים לזמן הסימולציה של האירוע הבא והסימולציה
הַכנָסוֹת.
עוזרים
הפרקים שלעיל הכירו אותך עם שונים ns-3 מושגי תכנות כגון חכם
מצביעים לניהול זיכרון נספר הפניות, תכונות, מרחבי שמות, התקשרויות חוזרות וכו'.
משתמשים שעובדים ב-API ברמה נמוכה זה יכולים להתחבר ns-3 חפצים עם גרנולריות עדינה.
עם זאת, תוכנית סימולציה שנכתבה כולה באמצעות ה-API ברמה נמוכה תהיה ארוכה למדי
ומייגע לקוד. מסיבה זו, מה שנקרא "ממשק API של עוזר" הועלה
על הליבה ns-3 ממשק API. אם קראת את ns-3 הדרכה, אתה כבר תכיר
עם ממשק ה-API של עוזר, מכיוון שזה ה-API שמשתמשים חדשים בדרך כלל מתוודעים אליו לראשונה.
בפרק זה, אנו מציגים את פילוסופיית העיצוב של ממשק ה-API של העוזר ומעמידים אותה בניגוד
ה-API ברמה נמוכה. אם תהפוך למשתמש כבד של ns-3, סביר להניח שתזוז קדימה ואחורה
בין ממשקי API אלה אפילו באותה תוכנית.
לממשק ה-API של עוזר יש כמה מטרות:
1. שאר src / אין תלות ב-API של עוזר; כל דבר שאפשר לעשות איתו
ניתן לקודד את ה-helper API גם ב-API ברמה נמוכה
2. מיכלים: לעתים קרובות סימולציות יצטרכו לבצע מספר פעולות זהות לקבוצות
של חפצים. ה-API של עוזר עושה שימוש רב במיכלים של אובייקטים דומים אליהם
ניתן לבצע פעולות דומות או זהות.
3. ממשק ה-API של עוזר אינו גנרי; הוא אינו שואף למקסם את השימוש החוזר בקוד. לכן,
מבני תכנות כגון פולימורפיזם ותבניות המשיגות שימוש חוזר בקוד הם
לא נפוץ באותה מידה. לדוגמה, ישנם עוזרי CsmaNetDevice נפרדים ו
עוזרי PointToPointNetDevice אך הם אינם נובעים מבסיס NetDevice משותף
מעמד.
4. ממשק ה-API של העזר עובד בדרך כלל עם אובייקטים שהוקצו מחסנית (לעומת ערימה). ל
כמה תוכניות, ns-3 ייתכן שמשתמשים לא יצטרכו לדאוג לגבי יצירת אובייקט ברמה נמוכה או
טיפול Ptr; הם יכולים להסתפק במיכלים של חפצים ובעוזרים שהוקצו בערימה
שפועלים עליהם.
ממשק ה-API של עוזר עוסק ביצירה ns-3 תוכניות קלות יותר לכתיבה ולקריאה, בלי
לוקח את הכוח של הממשק ברמה נמוכה. שאר פרק זה מספק כמה
דוגמאות למוסכמות התכנות של ממשק ה-API של עוזר.
עשייה חלק באמצעות מה היא גנופלוט כיתה
ישנן 2 שיטות נפוצות ליצירת עלילה באמצעות ns-3 וגנופלוט (-
http://www.gnuplot.info):
1. צור קובץ שליטה של Gnuplot באמצעות ns-3כיתת גנופלוט.
2. צור קובץ נתוני gnuplot באמצעות ערכים שנוצרו על ידי ns-3.
סעיף זה עוסק בשיטה 1, כלומר הוא עוסק כיצד ליצור עלילה באמצעות ns-3של גנופלוט
מעמד. אם אתה מעוניין בשיטה 2, עיין בסעיף המשנה "דוגמה אמיתית" בסעיף
קטע "מעקב" ב- ns-3 הדרכה.
יוצרים חלק שימוש מה היא גנופלוט כיתה
יש לבצע את השלבים הבאים על מנת ליצור עלילה באמצעות ns-3הכיתה של Gnuplot:
1. שנה את הקוד שלך כך שישתמש במחלקה Gnuplot ובפונקציות שלה.
2. הפעל את הקוד שלך כך שהוא יוצר קובץ בקרת gnuplot.
3. התקשר ל-gnuplot עם שם קובץ ה-gnuplot.
4. הצג את קובץ הגרפיקה שהופק במציג הגרפיקה המועדף עליך.
ראה את הקוד מהחלקות לדוגמה הנדונות להלן לפרטים על שלב 1.
An דוגמה תָכְנִית זֶה אתה משתמש מה היא גנופלוט כיתה
תוכנית לדוגמה המשתמשת ns-3ניתן למצוא את הכיתה של Gnuplot כאן:
src/stats/examples/gnuplot-example.cc
כדי להפעיל דוגמה זו, בצע את הפעולות הבאות:
מעטפת $ ./ואף
$ cd build/debug/src/stats/examples
$ ./gnuplot-example
זה אמור לייצר את קבצי הבקרה הבאים של Gnuplot בספרייה שבה הדוגמה
ממוקם:
plot-2d.plt
plot-2d-with-error-bars.plt
plot-3d.plt
על מנת לעבד את קבצי בקרת הגנופלוט הללו, בצע את הפעולות הבאות:
$ gnuplot plot-2d.plt
$ gnuplot plot-2d-with-error-bars.plt
$ gnuplot plot-3d.plt
זה אמור לייצר את הקבצים הגרפיים הבאים בספרייה שבה נמצאת הדוגמה
ממוקם:
plot-2d.png
plot-2d-with-error-bars.png
plot-3d.png
אתה יכול להציג קבצי גרפיקה אלה במציג הגרפיקה המועדף עליך. אם יש לך גימפ
מותקן במחשב שלך, למשל, תוכל לעשות זאת:
$ gimp plot-2d.png
$ gimp plot-2d-with-error-bars.png
$ gimp plot-3d.png
An דוגמה תלת מימד מגרש
העלילה הדו-מימדית הבאה
[תמונה]
נוצר באמצעות הקוד הבא מ-gnuplot-example.cc:
שימוש במרחב השמות std;
string fileNameWithNoExtension = "plot-2d";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "עלילה דו-ממדית";
string dataTitle = "2-D Data";
// הצג את העלילה וקבע את הכותרת שלה.
עלילת Gnuplot (graphicsFileName);
plot.SetTitle (plotTitle);
// צור את קובץ הגרפיקה, שקובץ העלילה יצור כאשר הוא
// משמש עם Gnuplot, היה קובץ PNG.
plot.SetTerminal ("png");
// הגדר את התוויות עבור כל ציר.
plot.SetLegend ("ערכי X", "ערכי Y");
// הגדר את הטווח עבור ציר ה-x.
plot.AppendExtra ("הגדר xrange [-6:+6]");
// הצג מופע של מערך הנתונים, הגדר את הכותרת שלו והפוך את הנקודות להיות
// משורטט יחד עם קווים מקשרים.
מערך נתונים של Gnuplot2dDataset;
dataset.SetTitle (dataTitle);
dataset.SetStyle (Gnuplot2dDataset::LINES_POINTS);
כפול x;
כפול y;
// צור את מערך הנתונים הדו-ממדי.
עבור (x = -5.0; x <= +5.0; x += 1.0)
{
// חשב את העקומה הדו-ממדית
//
// 2
// y = x .
//
y = x * x;
// הוסף נקודה זו.
dataset.Add (x, y);
}
// הוסף את מערך הנתונים לעלילה.
plot.AddDataset (ערכת נתונים);
// פתח את קובץ העלילה.
ofstream plotFile (plotFileName.c_str());
// כתוב את קובץ העלילה.
plot.GenerateOutput (plotFile);
// סגור את קובץ העלילה.
plotFile.close ();
An דוגמה תלת מימד מגרש עם שְׁגִיאָה ברים
העלילה הדו-ממדית הבאה עם פסי שגיאה בכיווני x ו-y
[תמונה]
נוצר באמצעות הקוד הבא מ-gnuplot-example.cc:
שימוש במרחב השמות std;
string fileNameWithNoExtension = "plot-2d-with-error-bars";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "עלילה דו-ממדית עם פסי שגיאה";
string dataTitle = "נתונים דו-ממדיים עם פסי שגיאה";
// הצג את העלילה וקבע את הכותרת שלה.
עלילת Gnuplot (graphicsFileName);
plot.SetTitle (plotTitle);
// צור את קובץ הגרפיקה, שקובץ העלילה יצור כאשר הוא
// משמש עם Gnuplot, היה קובץ PNG.
plot.SetTerminal ("png");
// הגדר את התוויות עבור כל ציר.
plot.SetLegend ("ערכי X", "ערכי Y");
// הגדר את הטווח עבור ציר ה-x.
plot.AppendExtra ("הגדר xrange [-6:+6]");
// הצג מופע של מערך הנתונים, הגדר את הכותרת שלו והפוך את הנקודות להיות
// משורטט ללא קווים מקשרים.
מערך נתונים של Gnuplot2dDataset;
dataset.SetTitle (dataTitle);
dataset.SetStyle (Gnuplot2dDataset::POINTS);
// הפוך את מערך הנתונים לכלול פסי שגיאה גם בכיווני x וגם ב-y.
dataset.SetErrorBars (Gnuplot2dDataset::XY);
כפול x;
כפול xErrorDelta;
כפול y;
כפול yErrorDelta;
// צור את מערך הנתונים הדו-ממדי.
עבור (x = -5.0; x <= +5.0; x += 1.0)
{
// חשב את העקומה הדו-ממדית
//
// 2
// y = x .
//
y = x * x;
// הפוך את אי הוודאות בכיוון x להיות קבועה והפוך
// אי הוודאות בכיוון y תהיה חלק קבוע של
// הערך של y.
xErrorDelta = 0.25;
yErrorDelta = 0.1 * y;
// הוסף נקודה זו עם אי ודאויות הן ב-x והן ב-y
// כיוון.
dataset.Add (x, y, xErrorDelta, yErrorDelta);
}
// הוסף את מערך הנתונים לעלילה.
plot.AddDataset (ערכת נתונים);
// פתח את קובץ העלילה.
ofstream plotFile (plotFileName.c_str());
// כתוב את קובץ העלילה.
plot.GenerateOutput (plotFile);
// סגור את קובץ העלילה.
plotFile.close ();
An דוגמה תלת מימד מגרש
העלילה הדו-מימדית הבאה
[תמונה]
נוצר באמצעות הקוד הבא מ-gnuplot-example.cc:
שימוש במרחב השמות std;
string fileNameWithNoExtension = "plot-3d";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "עלילה דו-ממדית";
string dataTitle = "3-D Data";
// הצג את העלילה וקבע את הכותרת שלה.
עלילת Gnuplot (graphicsFileName);
plot.SetTitle (plotTitle);
// צור את קובץ הגרפיקה, שקובץ העלילה יצור כאשר הוא
// משמש עם Gnuplot, היה קובץ PNG.
plot.SetTerminal ("png");
// סובב את העלילה 30 מעלות סביב ציר ה-x ולאחר מכן סובב את
// צייר 120 מעלות סביב ציר ה-Z החדש.
plot.AppendExtra ("תצוגת סט 30, 120, 1.0, 1.0");
// הפוך את האפס לציר z במישור ציר x ו-y.
plot.AppendExtra ("קבע ticslevel 0");
// הגדר את התוויות עבור כל ציר.
plot.AppendExtra ("הגדר xlabel 'X Values'");
plot.AppendExtra ("סט ylabel 'Y Values'");
plot.AppendExtra ("הגדר zlabel 'Z Values'");
// הגדר את הטווחים עבור ציר x ו-y.
plot.AppendExtra ("הגדר xrange [-5:+5]");
plot.AppendExtra ("סט yrange [-5:+5]");
// הצג מופע של מערך הנתונים, הגדר את הכותרת שלו והפוך את הנקודות להיות
// מחוברים בקווים.
מערך נתונים של Gnuplot3dDataset;
dataset.SetTitle (dataTitle);
dataset.SetStyle ("עם קווים");
כפול x;
כפול y;
כפול z;
// צור את מערך הנתונים הדו-ממדי.
עבור (x = -5.0; x <= +5.0; x += 1.0)
{
עבור (y = -5.0; y <= +5.0; y += 1.0)
{
// חשב את המשטח התלת-ממדי
//
// 2 2
// z = x * y .
//
z = x * x * y * y;
// הוסף נקודה זו.
dataset.Add (x, y, z);
}
// השורה הריקה נחוצה בסוף הנתונים של כל ערך x
// נקודות כדי שהרשת התלת-ממדית תפעל.
dataset.AddEmptyLine ();
}
// הוסף את מערך הנתונים לעלילה.
plot.AddDataset (ערכת נתונים);
// פתח את קובץ העלילה.
ofstream plotFile (plotFileName.c_str());
// כתוב את קובץ העלילה.
plot.GenerateOutput (plotFile);
// סגור את קובץ העלילה.
plotFile.close ();
שימוש פיתון ל הפעלה ns-3
כריכות Python מאפשרות להיכנס לקוד C++ ns-3 להיקרא מ-Python.
פרק זה מראה לך כיצד ליצור סקריפט Python שיכול לפעול ns-3 וגם
תהליך של יצירת כריכות Python עבור C++ ns-3 מודול.
מבוא
המטרה של כריכות Python עבור ns-3 הם כפולים:
1. אפשר למתכנת לכתוב סקריפטים של סימולציה שלמים בפייתון (-
http://www.python.org);
2. אב טיפוס לדגמים חדשים (למשל פרוטוקולי ניתוב).
לעת עתה, המוקד העיקרי של הכריכות הוא המטרה הראשונה, אבל השנייה
המטרה תתמוך בסופו של דבר גם כן. כריכות פייתון עבור ns-3 מפותחים
באמצעות כלי חדש בשם PyBindGen (http://code.google.com/p/pybindgen).
An דוגמה פיתון תסריט זֶה ריצות ns-3
הנה קוד לדוגמה שנכתב ב-Python ופועל ns-3, אשר כתוב
ב-C++. ניתן למצוא דוגמה זו של Python ב examples/tutorial/first.py:
ייבוא ns.applications
ייבוא ns.core
ייבוא ns.internet
ייבוא ns.network
ייבוא ns.point_to_point
ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO)
ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO)
nodes = ns.network.NodeContainer()
צמתים.צור(2)
pointToPoint = ns.point_to_point.PointToPointHelper()
pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
pointToPoint.SetChannelAttribute("Delay", ns.core.StringValue("2ms"))
devices = pointToPoint.Install(nodes)
stack = ns.internet.InternetStackHelper()
stack.Install(צמתים)
כתובת = ns.internet.Ipv4AddressHelper()
address.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
ממשקים = כתובת.הקצאה (מכשירים);
echoServer = ns.applications.UdpEchoServerHelper(9)
serverApps = echoServer.Install(צמתים. קבל(1))
serverApps.Start(ns.core.Seconds(1.0))
serverApps.Stop(ns.core.Seconds(10.0))
echoClient = ns.applications.UdpEchoClientHelper(interfaces.GetAddress(1), 9)
echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))
clientApps = echoClient.Install(צמתים. קבל(0))
clientApps.Start(ns.core.Seconds(2.0))
clientApps.Stop(ns.core.Seconds(10.0))
ns.core.Simulator.Run()
ns.core.Simulator.Destroy()
ריצה פיתון סקריפטים
waf מכיל כמה אפשרויות שמעדכנות אוטומטית את נתיב python כדי למצוא את ה-ns3
מודול. כדי להפעיל תוכניות לדוגמה, יש שתי דרכים להשתמש ב- waf כדי לטפל בזה. אחד
הוא להפעיל מעטפת וואף; לְמָשָׁל:
$ ./waf --shell
$ python examples/wireless/mixed-wireless.py
והשני הוא להשתמש באפשרות --pyrun ל-waf:
$ ./waf --pyrun examples/wireless/mixed-wireless.py
כדי להפעיל סקריפט python תחת באגים C:
$ ./waf --shell
$ gdb --args python examples/wireless/mixed-wireless.py
להריץ סקריפט Python משלך שקורא ns-3 ויש לזה את הדרך הזו,
/path/to/your/example/my-script.py, תעשה את הדברים הבאים:
$ ./waf --shell
$ python /path/to/your/example/my-script.py
אזהרות
כריכות פייתון עבור ns-3 הם עבודה בתהליך, וכמה מגבלות ידועות על ידי
מפתחים. חלק מהמגבלות הללו (לא כולן) מפורטות כאן.
לא שלם סיקור
קודם כל, זכור שלא 100% מה-API נתמך ב-Python. חלק מה
הסיבות הן:
1. חלק מממשקי ה-API כוללים מצביעים, שדורשים ידע באיזה סוג זיכרון
סמנטיקה עוברת (מי הבעלים של איזה זיכרון). ידע כזה אינו חלק מהפונקציה
חתימות, והוא מתועד או לפעמים אפילו לא מתועד. הערות הן
נדרש לקשור את הפונקציות הללו;
2. לפעמים נעשה שימוש בסוג נתונים בסיסי יוצא דופן או במבנה C++ שעדיין לא נעשה
נתמך על ידי PyBindGen;
3. GCC-XML אינו מדווח על מחלקות מבוססות תבניות אלא אם כן הן מוכנות.
את רוב ממשקי ה-API החסרים ניתן לעטוף, לתת מספיק זמן, סבלנות ומומחיות, וכן
ככל הנראה ייעטף אם יוגשו דוחות באגים. עם זאת, אל תגיש דוח באג
אומר "הכריכות אינן שלמות", כי אין לנו כוח אדם להשלים 100% מה
כריכות.
המרה קונסטרוקטורים
המרה בנאים עדיין לא נתמכים באופן מלא על ידי PyBindGen, והם תמיד פועלים כמו
בנאים מפורשים בעת תרגום API לפייתון. לדוגמה, ב-C++ אתה יכול לעשות
זֶה:
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase ("192.168.0.0", "255.255.255.0");
ipAddrs.Assign (backboneDevices);
ב-Python, לעת עתה עליך לעשות:
ipAddrs = ns3.Ipv4AddressHelper()
ipAddrs.SetBase(ns3.Ipv4Address("192.168.0.0"), ns3.Ipv4Mask("255.255.255.0"))
ipAddrs.Assign(backboneDevices)
CommandLine
CommandLine::AddValue() עובד אחרת ב-Python מאשר ב-Python ns-3. ב- Python, ה-
הפרמטר הראשון הוא מחרוזת שמייצגת את שם אפשרות שורת הפקודה. כאשר האפשרות
מוגדר, תכונה בעלת שם זהה לשם האופציה מוגדרת ב- CommandLine()
לְהִתְנַגֵד. דוגמא:
NUM_NODES_SIDE_DEFAULT = 3
cmd = ns3.CommandLine()
cmd.NumNodesSide = אין
cmd.AddValue("NumNodesSide", "מספר צמתים בצד הרשת (המספר הכולל של צמתים יהיה המספר הזה בריבוע)")
cmd.Parse(argv)
[...]
אם cmd.NumNodesSide הוא ללא:
num_nodes_side = NUM_NODES_SIDE_DEFAULT
else
num_nodes_side = int(cmd.NumNodesSide)
מעקב
מעקב מבוסס התקשרות חוזר אינו נתמך עדיין כראוי עבור Python, כמו חדש ns-3 API צריך
להינתן כדי לתמוך בכך.
כתיבת קבצי Pcap נתמכת באמצעות ה-API הרגיל.
מאז נתמך מעקב Ascii ns-3.4 דרך ה-API הרגיל של C++ מתורגם לפייתון.
עם זאת, מעקב אחר ascii מצריך יצירת אובייקט Ostream כדי לעבור לתוך ascii
שיטות איתור. ב-Python, ה-C++ std::ofstream עטוף באופן מינימלי כדי לאפשר
זֶה. לדוגמה:
ascii = ns3.ofstream("wifi-ap.tr") # צור את הקובץ
ns3.YansWifiPhyHelper.EnableAsciiAll(ascii)
ns3.Simulator.Run()
ns3.Simulator.Destroy()
ascii.close() # סגור את הקובץ
יש אזהרה אחת: אסור לאפשר לאובייקט הקובץ לאסוף אשפה בזמן ns-3
עדיין משתמש בו. זה אומר שאסור לאפשר למשתנה 'ascii' למעלה ללכת
מחוץ לתחום, אחרת התוכנית תקרוס.
Cygwin הגבלה
כריכות פייתון לא עובדות על Cygwin. זה נובע מבאג gccxml.
אתה עשוי להתחמק מזה על ידי סריקה מחדש של הגדרות API מתוך ה-cygwin
סביבה (./waf --python-scan). עם זאת, הפתרון הסביר ביותר יצטרך כנראה
להיות שאנו משביתים כריכות פיתון ב-CygWin.
אם באמת אכפת לך מכריכות Python ב-Windows, נסה לבנות עם mingw ו-native
פיתון במקום. לחלופין, כדי לבנות ללא כריכות פיתון, השבת כריכות פיתון ב-
שלב ההגדרה:
$ ./waf configure --disable-python
עבודה עם פיתון כריכות
יש כרגע שני סוגים של כריכות Python ns-3:
1. כריכות מונוליטיות מכילות הגדרות API עבור כל המודולים וניתן למצוא אותן ב
ספרייה אחת, כריכות/פיתון.
2. קישורים מודולריים מכילים הגדרות API עבור מודול בודד וניתן למצוא אותם בכל אחד מהם
מודולים כריכות במדריך.
פיתון כריכות זרימת עבודה
התהליך שבו מטופלות כריכות Python הוא הבא:
1. מעת לעת מפתח משתמש ב-GCC-XML (http://www.gccxml.org) סריקת API מבוססת
script, ששומר את הגדרת ה-API הסרוק כ bindings/python/ns3_module_*.py קבצים
או כקבצי Python בכל מודולים' כריכות מַדרִיך. קבצים אלה נשמרים מתחת
בקרת גרסאות בעיקר ns-3 מאגר;
2. מפתחים אחרים משכפלים את המאגר ומשתמשים בהגדרות ה-API שכבר נסרקו;
3. בעת הגדרת התצורה ns-3, pybindgen יוריד אוטומטית אם עדיין לא
מוּתקָן. מְשׁוּחרָר ns-3 tarballs ישלח עותק של pybindgen.
אם משהו משתבש עם קומפילציה של כריכות Python ואתה פשוט רוצה להתעלם מהם
ולהמשיך הלאה עם C++, אתה יכול להשבית את Python עם:
$ ./waf --disable-python
הוראות ל טיפול חדש קבצים or השתנה ה-API של
אז שינית את הקיים ns-3 ממשקי API וקשרי Python כבר לא קומפילים? לַעֲשׂוֹת
אל ייאוש, אתה יכול לסרוק מחדש את הכריכות כדי ליצור כריכות חדשות המשקפות את השינויים
אל ה ns-3 ה-API.
תלוי אם אתה משתמש בכריכות מונוליטיות או מודולריות, עיין בדיונים להלן
למד כיצד לסרוק מחדש את כריכות ה-Python שלך.
מוֹנוֹלִיטִי פיתון כריכות
סריקה מה היא מוֹנוֹלִיטִי פיתון כריכות
כדי לסרוק את כריכות Python המונוליטיות בצע את הפעולות הבאות:
$ ./waf --python-scan
ארגון of מה היא מוֹנוֹלִיטִי פיתון כריכות
ההגדרות המונוליטיות של Python API מאורגנות באופן הבא. לכל אחד ns-3 מודול
, הקובץ bindings/python/ns3_module_ .py מתאר את ה-API שלו. כל אחד מאלה
לקבצים יש 3 פונקציות ברמה העליונה:
1. def register_types(מודול)(): פונקציה זו דואגת לרישום סוגים חדשים (למשל
מחלקות C++, enums) המוגדרות במודול הזה;
2. def register_methods(מודול)(): פונקציה זו קוראת, עבור כל מחלקה , אחר
function register_methods_Ns3 (מודול). הפונקציות האחרונות הללו מוסיפות שיטה
הגדרות לכל מחלקה;
3. def register_functions(מודול)(): פונקציה זו רושמת ns-3 פונקציות ששייכות ל
המודול הזה.
מודולרי פיתון כריכות
סקירה כללית
מאז ns 3.11, הכריכות המודולריות מתווספות, במקביל למונוליט הישן
כריכות.
כריכות הפיתון החדשות נוצרות לתוך מרחב שמות 'ns', במקום 'ns3' עבור הישן
כריכות. דוגמא:
מ-ns.network import Node
n1 = Node()
עם כריכות פייתון מודולריות:
1. לכל אחד יש מודול הרחבה נפרד של Python ns-3 מודול;
2. סריקת הגדרות API (apidefs) נעשית על בסיס מודול ns;
3. קבצי ה-apidefs של כל מודול מאוחסנים בתיקייה משנה 'bindings' של המודול
ספרייה;
סריקה מה היא מודולרי פיתון כריכות
כדי לסרוק את כריכות Python המודולריות עבור מודול הליבה, למשל, בצע את הפעולות הבאות:
$ ./waf --apiscan=core
כדי לסרוק את כריכות Python המודולריות עבור כל המודולים, בצע את הפעולות הבאות:
$ ./waf --apiscan=all
יוצרים a חדש מודול
אם אתה מוסיף מודול חדש, כריכות Python ימשיכו להדר אך לא
לכסות את המודול החדש.
כדי לכסות מודול חדש, עליך ליצור א bindings/python/ns3_module_ .py קובץ,
בדומה למה שמתואר בסעיפים הקודמים, ורשום אותו במשתנה
LOCAL_MODULES() in bindings/python/ns3modulegen.py
מוסיף מודולרי כריכות ל A קיים מודול
כדי להוסיף תמיכה בכריכות מודולריות לקיים ns-3 מודול, פשוט הוסף את הדברים הבאים
שורה לפונקציית ה-wscript build() שלו:
bld.ns3_python_bindings()
ארגון of מה היא מודולרי פיתון כריכות
השמיים src/ /כריכות הספרייה עשויה להכיל את הקבצים הבאים, חלקם
אופציונאלי:
· callbacks_list.py: זהו קובץ סרוק, אל תיגע. מכיל רשימה של
Callback<...> מופעי תבנית שנמצאו בכותרות הסרוקות;
· modulegen__gcc_LP64.py: זהו קובץ סרוק, אל תיגע. הגדרות API סרוקות
עבור ארכיטקטורת GCC, LP64 (64 סיביות)
· modulegen__gcc_ILP32.py: זהו קובץ סרוק, אל תיגע. הגדרות API סרוקות
עבור ארכיטקטורת GCC, ILP32 (32 סיביות)
· modulegen_customizations.py: באפשרותך להוסיף קובץ זה כדי להתאים אישית את הקובץ
יצירת קוד pybindgen
· scan-header.h: באפשרותך להוסיף קובץ זה כדי להתאים אישית איזה קובץ כותרת נסרק
עבור המודול. בעיקרון הקובץ הזה נסרק במקום ns3/ -מודול.ח.
בדרך כלל, ההצהרה הראשונה היא #include "ns3/ -module.h", ועוד כמה אחרים
דברים כדי לאלץ מופעי תבנית;
· module_helpers.cc: אתה יכול להוסיף קבצים נוספים, כגון זה, שיקושרו לפייתון
מודול הרחבה, אך עליהם להיות רשומים ב-wscript. להסתכל על
src/core/wscript לדוגמא כיצד לעשות זאת;
· .py: אם הקובץ הזה קיים, הוא הופך למודול פיתון "חזיתי" עבור ה-ns3
מודול, ומודול ההרחבה (קובץ.so) הופך ל-_ .אז במקום .כך.
ה קובץ .py צריך לייבא את כל הסמלים מהמודול _ (זה יותר
מסובך ממה שזה נשמע, ראה src/core/bindings/core.py לדוגמא), ואז תוכל להוסיף
כמה הגדרות נוספות של פיתון טהור.
עוד מֵידָע ל מפתחים
אם אתה מפתח וצריך מידע נוסף על ns-3כריכות של Python, אנא עיין ב
פיתון כריכות ויקי עמוד.
בדיקות
סקירה כללית
מסמך זה עוסק בבדיקה ובאימות של ns-3 תוֹכנָה.
מסמך זה מספק
· רקע על מינוח ובדיקות תוכנה (פרק 2);
· תיאור של מסגרת הבדיקות ns-3 (פרק 3);
· מדריך למפתחי מודלים או תורמים למודלים חדשים כיצד לכתוב מבחנים (פרק
4);
בקצרה, שלושת הפרקים הראשונים צריכים להיקרא על ידי מפתחים ותורמים של ns אשר
צריך להבין כיצד לתרום קוד בדיקה ותוכניות מאומתות, והשאר
של המסמך מספק מקום לאנשים לדווח על היבטים של דגמים נבחרים
אושרו.
רקע
זֶה פרק אולי be דילג by הקוראים מוכר עם מה היא יסודות of תוֹכנָה בדיקה.
כתיבת תוכנה ללא פגמים היא הצעה קשה. יש הרבה ממדים ל
בעיה ויש בלבול רב בנוגע למה הכוונה במונחים שונים ב
הקשרים שונים. מצאנו שכדאי להשקיע קצת זמן בסקירת
נושא והגדרת כמה מונחים.
בדיקת תוכנה עשויה להיות מוגדרת באופן רופף כתהליך של ביצוע תוכנית עם
כוונה למצוא שגיאות. כאשר נכנסים לדיון לגבי בדיקות תוכנה, זה
מהר מאוד מתברר שיש הרבה מערכות חשיבה שונות שבהן אפשר
לגשת לנושא.
לדוגמה, אפשר לחלק את התהליך לקטגוריות פונקציונליות רחבות כמו
''בדיקת תקינות'' ''בדיקת ביצועים'' ''בדיקת תקינות'' ו''אבטחה
בדיקה.'' דרך נוספת להסתכל על הבעיה היא לפי מחזור חיים: ''בדיקות דרישות''
''בדיקות עיצוב'' ''בדיקות קבלה'' ו''בדיקות תחזוקה'' עוד השקפה
הוא לפי היקף המערכת שנבדקה. במקרה זה אפשר לדבר על ''בדיקת יחידות''
''בדיקת רכיבים'' ''בדיקת אינטגרציה'' ו''בדיקת מערכת''. מונחים אלה הם
גם לא מתוקנן בשום צורה, וכך ''בדיקות תחזוקה'' ו''רגרסיה
בדיקה'' עשויה להישמע לסירוגין. בנוסף, לעתים קרובות נעשה שימוש לרעה במונחים אלה.
ישנן גם מספר גישות פילוסופיות שונות לבדיקות תוכנה. ל
לדוגמה, ארגונים מסוימים דוגלים בכתיבת תוכניות מבחן לפני יישום בפועל
התוכנה הרצויה, מניבה ''פיתוח מונחה מבחן''. ארגונים מסוימים דוגלים
בדיקה מנקודת מבט של לקוח בהקדם האפשרי, בעקבות הקבלה ל-
תהליך פיתוח זריז: ''בדוק מוקדם ובדוק לעתים קרובות.'' זה נקרא לפעמים
''בדיקות זריזות'' נראה שיש לפחות גישה אחת לבדיקה לכל אחד
מתודולוגיית פיתוח.
השמיים ns-3 הפרויקט אינו בעניין של תמיכה באחד מהתהליכים הללו, אבל
לפרויקט בכללותו יש דרישות שעוזרות ליידע את תהליך הבדיקה.
כמו כל מוצרי התוכנה הגדולים, ns-3 יש מספר תכונות שחייבות להיות נוכחות עבורן
המוצר להצליח. מנקודת מבט של בדיקה, כמה מהתכונות הללו חייבות להיות
המטופלים הם אלה ns-3 חייב להיות ''נכון'' ''חזק,'' ''ביצועים'' וכן
''ניתן לתחזוקה'' באופן אידיאלי צריכים להיות מדדים עבור כל אחד מהממדים האלה שכן
נבדק על ידי הבדיקות כדי לזהות מתי המוצר לא עומד בציפיות שלו /
דרישות.
נכונות
המטרה העיקרית של הבדיקה היא לקבוע שחלק מהתוכנה מתנהגת
''נכון.'' עבור ns-3 זה אומר שאם אנחנו מדמים משהו, הסימולציה צריכה
מייצגים נאמנה ישות פיזית או תהליך לפי דיוק מוגדר ו
דיוק.
מסתבר שיש שתי נקודות מבט שמהן אפשר לראות את הנכונות.
אימות שמודל מסוים מיושם על פי המפרט שלו הוא
נקרא באופן כללי אימות. תהליך ההחלטה שהדגם מתאים לו
השימוש המיועד שלו נקרא באופן כללי אימות.
בדיקת מערכות ו אימות
מודל ממוחשב הוא ייצוג מתמטי או לוגי של משהו. זה יכול
מייצג רכב, פיל (ראה דוד של הראל לדבר על דוגמנות an פיל at
SIMUTools 2009, או כרטיס רשת. מודלים יכולים גם לייצג תהליכים כמו גלובליים
התחממות, זרימת תנועה בכביש המהיר או מפרט של פרוטוקול רשת. מודלים יכולים להיות
ייצוגים נאמנים לחלוטין של מפרט תהליך לוגי, אבל הם
בהכרח לעולם לא יוכל לדמות לחלוטין אובייקט או תהליך פיזי. ברוב המקרים, א
מספר הפשטות נעשות במודל כדי לבצע סימולציה באופן חישובי
מְמוּשׁמָע.
לכל דגם יש א יעד מערכת שהוא מנסה לדמות. הצעד הראשון פנימה
יצירת מודל סימולציה היא לזהות את מערכת היעד הזו ואת רמת הפירוט ו
דיוק שהסימולציה רצויה לשחזר. במקרה של תהליך הגיוני,
מערכת היעד עשויה להיות מזוהה כ''TCP כפי שהוגדר על ידי RFC 793.'' במקרה זה, זה
סביר להניח שיהיה רצוי ליצור דגם שמשחזר RFC באופן מלא ונאמן
793. במקרה של תהליך פיזי זה לא יתאפשר. אם, למשל, אתה
רוצה לדמות כרטיס רשת אלחוטית, אתה עשוי לקבוע שאתה צריך, ''an
יישום מדויק ברמת MAC של מפרט 802.11 ו... לא כל כך איטי
דגם ברמת PHY של מפרט 802.11a.''
ברגע שזה נעשה, אפשר לפתח מודל מופשט של מערכת היעד. זה
בדרך כלל תרגיל בניהול הפערים בין מורכבות ודרישות משאבים
ודיוק. תהליך פיתוח מודל מופשט נקרא מודל
הכשרה בספרות. במקרה של פרוטוקול TCP, תהליך זה מביא ל-
עיצוב עבור אוסף של אובייקטים, אינטראקציות והתנהגויות שיישמו במלואם
RFC 793 אינץ' ns-3. במקרה של הכרטיס האלחוטי, תהליך זה מביא למספר של
פשרות כדי לאפשר הדמיה של השכבה הפיזית ועיצוב של התקן רשת
וערוץ עבור ns-3, יחד עם האובייקטים, האינטראקציות וההתנהגויות הרצויות.
מודל מופשט זה מפותח לאחר מכן ל- an ns-3 מודל שמיישם את המופשט
מודל כתוכנת מחשב. תהליך קבלת היישום להסכים עם
מודל מופשט נקרא מודל אימות בספרות.
התהליך עד כה הוא לולאה פתוחה. מה שנותר הוא לקבוע כי ns-3 נתון
למודל יש קשר כלשהו למציאות כלשהי -- שמודל הוא ייצוג מדויק שלה
מערכת אמיתית, בין אם מדובר בתהליך לוגי ובין אם מדובר בישות פיזית.
אם מתכוונים להשתמש במודל סימולציה כדי לנסות ולחזות איך מתנהלת מערכת אמיתית כלשהי
כדי להתנהג, חייבת להיות סיבה כלשהי להאמין בתוצאות שלך -- כלומר, האם אפשר לסמוך על זה
הסקה שנעשתה מהמודל מתורגמת לתחזית נכונה עבור המערכת האמיתית.
תהליך לגרום להתנהגות מודל ns-3 להסכים עם מערכת היעד הרצויה
התנהגות כפי שהוגדרה על ידי תהליך ההסמכה של המודל נקראת מודל אימות ב
סִפְרוּת. במקרה של יישום TCP, ייתכן שתרצה להשוות את ההתנהגות של
מודל ns-3 TCP שלך למימוש ייחוס כלשהו על מנת לאמת את המודל שלך. ב
במקרה של הדמיית שכבה פיזית אלחוטית, אולי כדאי להשוות את ההתנהגות של
הדגם שלך לזה של חומרה אמיתית בסביבה מבוקרת,
השמיים ns-3 סביבת בדיקה מספקת כלים המאפשרים גם אימות מודל וגם
בדיקות, ומעודד פרסום של תוצאות אימות.
חוסן
איתנות היא האיכות של היכולת לעמוד בלחצים, או שינויים בסביבות,
תשומות או חישובים וכו'. מערכת או עיצוב הם ''חזקים'' אם הם יכולים להתמודד עם כאלה
שינויים עם אובדן מינימלי של פונקציונליות.
סוג זה של בדיקות נעשה בדרך כלל עם מיקוד מסוים. לדוגמה, המערכת as
ניתן להריץ שלם על תצורות מערכת רבות ושונות כדי להוכיח שכן
ביצוע נכון במספר רב של סביבות.
המערכת יכולה להיות גם לחוצה על ידי הפעלה קרובה או מעבר לקיבולת על ידי ייצור
או הדמיית מיצוי משאבים מסוגים שונים. ז'אנר זה של בדיקות נקרא
''מבחן לחץ.''
המערכת ומרכיביה עלולים להיחשף למה שנקרא ''בדיקות נקיות'' המדגימות
תוצאה חיובית -- כלומר שהמערכת פועלת בצורה נכונה בתגובה לגדול
וריאציה של תצורות צפויות.
המערכת ומרכיביה עלולים להיות חשופים גם ל''בדיקות מלוכלכות'' המספקות תשומות
מחוץ לטווח הצפוי. לדוגמה, אם מודול מצפה למחרוזת אפס סיומת
ייצוג של מספר שלם, מבחן מלוכלך עשוי לספק מחרוזת אקראית ללא הפסקה
תווים כדי לוודא שהמערכת לא קורסת כתוצאה מקלט בלתי צפוי זה.
למרבה הצער, זיהוי קלט ''מלוכלך'' כזה ונקיטת אמצעי מניעה כדי להבטיח את
המערכת לא נכשלת בצורה קטסטרופלית יכולה לדרוש כמות עצומה של פיתוח תקורה.
על מנת לצמצם את זמן הפיתוח, התקבלה החלטה בשלב מוקדם של הפרויקט
למזער את כמות אימות הפרמטרים וטיפול בשגיאות ב- ns-3 בסיס קוד. ל
בגלל זה אנחנו לא מבזבזים זמן רב על בדיקות מלוכלכות -- זה רק יגלה את
תוצאות של החלטת העיצוב שאנו יודעים שלקחנו.
אנחנו כן רוצים להדגים את זה ns-3 התוכנה אכן פועלת על פני קבוצה מסוימת של תנאים. אָנוּ
שאלו כמה הגדרות כדי לצמצם את זה קצת. ה תחום of יָשִׂימוּת is
קבוצה של תנאים שנקבעו עבורם הדגם נבדק, לעומתם
המציאות במידת האפשר, ונמצאת כמתאימה לשימוש. ה רכס of דיוק הוא
הסכם בין המודל הממוחשב למציאות בתחום של תחולה.
השמיים ns-3 סביבת בדיקה מספקת כלים המאפשרים הגדרה והרצה של בדיקה
סביבות על פני מספר מערכות (buildbot) ומספקת שיעורים לעידוד נקיון
בדיקות לאימות פעולת המערכת על פני ''תחום היישום'' הצפוי
ו''טווח דיוק.''
performant
אוקיי, ''performant'' היא לא מילה באנגלית אמיתית. עם זאת, זהו ניאולוגיזם תמציתי מאוד
זה משמש לעתים קרובות למדי כדי לתאר את מה שאנחנו רוצים ns-3 להיות: חזק ומהיר מספיק כדי
לעשות את העבודה.
זה באמת על הנושא הרחב של בדיקות ביצועי תוכנה. אחד המפתחות
מה שנעשה הוא להשוות בין שתי מערכות כדי למצוא מי מניבה ביצועים טובים יותר (ראה
אמות מידה). זה משמש כדי להדגים כי, למשל, ns-3 יכול לבצע סוג בסיסי
של סימולציה מהיר לפחות כמו כלי מתחרה, או שניתן להשתמש בו כדי לזהות חלקים של
המערכת שפועלת בצורה גרועה.
ב ns-3 מסגרת בדיקה, אנו מספקים תמיכה בתזמון סוגים שונים של בדיקות.
תחזוקת
מוצר תוכנה חייב להיות בר תחזוקה. זו, שוב, אמירה רחבה מאוד, אבל א
מסגרת בדיקה יכולה לעזור במשימה. לאחר שמודל פותח, אושר ו
מאומת, אנו יכולים לבצע שוב ושוב את חבילת הבדיקות עבור המערכת כולה כדי להבטיח
שהוא נשאר תקף ומאומת לאורך כל חייו.
כאשר תכונה מפסיקה לתפקד כמתוכנן לאחר שינוי כלשהו במערכת
משולב, זה נקרא באופן כללי א נסיגה. במקור המונח רגרסיה
התייחס לשינוי שגרם לבאג שתוקן בעבר להופיע מחדש, אך המונח כן
התפתח כדי לתאר כל סוג של שינוי ששובר את הפונקציונליות הקיימת. יש הרבה
סוגים של רגרסיות שעלולות להתרחש בפועל.
A מקומי נסיגה הוא כזה שבו שינוי משפיע ישירות על הרכיב שהשתנה. ל
לדוגמה, אם רכיב שונה כדי להקצות ולפנות זיכרון אבל מצביעים מיושנים כן
בשימוש, הרכיב עצמו נכשל.
A מרחוק נסיגה הוא כזה שבו שינוי לרכיב אחד מפרק את הפונקציונליות פנימה
מרכיב נוסף. זה משקף הפרה של טענה משתמעת אך אולי לא מוכרת
חוזה בין רכיבים.
An חשף נסיגה הוא כזה שיוצר מצב שבו באג קיים בעבר
שלא הייתה לו השפעה נחשף לפתע במערכת. זה עשוי להיות פשוט כמו פעילות גופנית
נתיב קוד בפעם הראשונה.
A ביצועים נסיגה הוא כזה שגורם לדרישות הביצועים של המערכת
להיות מופר. לדוגמה, ביצוע עבודה כלשהי בפונקציה ברמה נמוכה שעלולה לחזור על עצמה
מספר רב של פעמים עלול להפוך את המערכת לבלתי שמישה מנקודות מבט מסוימות.
השמיים ns-3 מסגרת בדיקה מספקת כלים לאוטומציה של התהליך המשמש לאימות ו
אמת את הקוד בחבילות בדיקה ליליות כדי לסייע בזיהוי מהיר של רגרסיות אפשריות.
בדיקות במסגרת
ns-3 מורכב ממנוע ליבת סימולציה, סט של דגמים, תוכניות לדוגמה ומבחנים.
עם הזמן, תורמים חדשים תורמים מודלים, בדיקות ודוגמאות. תוכנית בדיקה של Python
test.py משמש כמנהל ביצוע הבדיקה; test.py יכול להריץ קוד בדיקה ודוגמאות ל
חפש רגרסיות, יכול להפיק את התוצאות למספר צורות ויכול לנהל קוד
כלי ניתוח כיסוי. נוסף על זה, אנחנו שכבות Buildbots שהם בנייה אוטומטית
רובוטים המבצעים בדיקות חוסן על ידי הפעלת מסגרת הבדיקה במערכות שונות
ועם אפשרויות תצורה שונות.
BuildBots
ברמה הגבוהה ביותר של בדיקות ns-3 נמצאים ה-buildbots (רובוטים לבנות). אם אתה
לא מכיר את המערכת הזו להסתכל על http://djmitche.github.com/buildbot/docs/0.7.11/.
זוהי מערכת אוטומטית בקוד פתוח המאפשרת ns-3 להיבנות מחדש ולבדוק כל אחד
פעם שמשהו השתנה. על ידי הפעלת ה-buildbots על מספר מערכות שונות אנו
יכול להבטיח זאת ns-3 בונה ומבצע כהלכה על כל המערכות הנתמכות שלו.
משתמשים (ומפתחים) בדרך כלל לא יתקשרו עם מערכת ה-buildbot מלבד ל
קרא את ההודעות שלו לגבי תוצאות הבדיקה. אם מזוהה כשל באחד מה
עבודות בנייה ובדיקה אוטומטיות, ה-buildbot ישלח אימייל אל מפתחי ns
רשימת תפוצה. האימייל הזה ייראה בערך כמו
בכתובת הפרטים המלאים המוצגת במייל, אפשר לחפש את מילת המפתח נכשל ו
בחר stdio קישור לשלב המתאים כדי לראות את הסיבה לכשל.
ה-buildbot יעשה את עבודתו בשקט אם לא יהיו שגיאות, והמערכת תעבור
לבנות ולבדוק מחזורי כל יום כדי לוודא שהכל בסדר.
Test.py
ה-buildbots משתמשים בתוכנת Python, test.py, שאחראי להפעיל את כל
בדיקות ואיסוף הדוחות המתקבלים לצורה הניתנת לקריאה אנושית. התוכנית הזו היא
זמין גם לשימוש על ידי משתמשים ומפתחים גם כן.
test.py הוא מאוד גמיש בכך שהוא מאפשר למשתמש לציין את מספר וסוג הבדיקות
לָרוּץ; וגם כמות וסוג התפוקה להפקה.
לפני ריצה test.py, ודא שהדוגמאות והבדיקות של ns3 נבנו על ידי ביצוע
את הפעולות הבאות
$ ./waf configure --enable-examples --enable-tests
$ ./waf
כברירת מחדל, test.py יריץ את כל הבדיקות הזמינות וידווח על המצב בצורה תמציתית מאוד
טופס. הפעלת הפקודה
$ ./test.py
יביא למספר של PASS, FAIL, להתרסק or לדלג אינדיקציות ואחריו סוג של
הבדיקה שהורצה ושם התצוגה שלה.
Waf: כניסה לספרייה `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: עוזב את הספרייה `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'בנייה' הסתיים בהצלחה (0.939 שניות)
כשל: TestSuite ns3-wifi-propagation-loss-models
PASS: שירות אובייקט-שם TestSuite
PASS: TestSuite pcap-file-object
PASS: TestSuite ns3-tcp-cwnd
...
PASS: TestSuite ns3-tcp-interoperability
PASS: דוגמה לשידור csma
PASS: דוגמה ל-csma-multicast
מצב זה נועד לשמש משתמשים שמעוניינים לקבוע אם הם
ההפצה פועלת כהלכה, ועל ידי מפתחים שמעוניינים לקבוע אם
שינויים שהם ביצעו גרמו לרגרסיות כלשהן.
ישנן מספר אפשרויות זמינות לשלוט בהתנהגות test.py. אם אתה רץ
test.py - עזרה אתה אמור לראות תקציר פקודה כמו:
שימוש: test.py [אפשרויות]
אפשרויות:
-h, - עזרה להציג את הודעת העזרה הזו ולצאת ממנה
-b BUILDPATH, --buildpath=BUILDPATH
ציין את הנתיב שבו נבנה ns-3 (ברירת המחדל היא
בניית ספרייה עבור הגרסה הנוכחית)
-c KIND, --constrain=KIND
להגביל את רץ המבחן לפי סוג המבחן
-e EXAMPLE, --example=EXAMPLE
ציין דוגמה בודדת להפעלה (אין נתיב יחסי
נחוץ)
-g, --grind הפעל את חבילות הבדיקה והדוגמאות באמצעות valgrind
-k, --kinds מדפיסים את סוגי הבדיקות הזמינות
-l, --list הדפס את רשימת הבדיקות המוכרות
-m, -מרובים דווחו על כשלים מרובים מחבילות בדיקה ובדיקה
מקרים
-n, --nowaf אין להפעיל waf לפני תחילת הבדיקה
-p PYEXAMPLE, --pyexample=PYEXAMPLE
ציין דוגמה אחת של פיתון להפעלה (עם relative
נתיב)
-r, --retain שמור את כל הקבצים הזמניים (שהם בדרך כלל
נמחק)
-s TEST-SUITE, --suite=TEST-SUITE
ציין חבילת בדיקה אחת להפעלה
-t TEXT-FILE, --text=TEXT-FILE
כתוב תוצאות בדיקה מפורטות לתוך TEXT-FILE.txt
-v, --התקדמות הדפסה מפורטת והודעות מידע
-w HTML-FILE, --web=HTML-FILE, --html=HTML-FILE
כתוב תוצאות בדיקה מפורטות לתוך HTML-FILE.html
-x XML-FILE, --xml=XML-FILE
כתוב תוצאות בדיקה מפורטות לתוך XML-FILE.xml
אם מציינים סגנון פלט אופציונלי, אפשר ליצור תיאורים מפורטים של
בדיקות וסטטוס. סגנונות זמינים הם טֶקסט ו HTML. ה-buildbots יבחרו את ה-HTML
אפשרות להפקת דוחות בדיקות HTML עבור הבנייה הלילית באמצעות
$ ./test.py --html=nightly.html
במקרה זה, קובץ HTML בשם ''nightly.html'' ייווצר עם סיכום יפה
מהבדיקה שנעשתה. פורמט ''קריא אנושי'' זמין עבור משתמשים המתעניינים ב
פרטים.
$ ./test.py --text=results.txt
בדוגמה למעלה, חבילת הבדיקה הבודקת את ns-3 אובדן התפשטות המכשיר האלחוטי
דגמים נכשלו. כברירת מחדל לא מסופק מידע נוסף.
כדי להמשיך ולחקור את הכישלון, test.py מאפשר לציין חבילת בדיקה אחת.
הפעלת הפקודה
$ ./test.py --suite=ns3-wifi-propagation-loss-models
או באופן שווה
$ ./test.py -s ns3-wifi-propagation-loss-models
גורם לכך שחבילת בדיקה יחידה מופעלת.
כשל: TestSuite ns3-wifi-propagation-loss-models
כדי למצוא מידע מפורט לגבי הכשל, יש לציין את סוג הפלט
רצוי. לדוגמה, רוב האנשים כנראה יתעניינו בקובץ טקסט:
$ ./test.py --suite=ns3-wifi-propagation-loss-models --text=results.txt
זה יביא לכך שחבילת בדיקה יחידה תופעל עם סטטוס הבדיקה שנכתב ל-
הקובץ ''results.txt''.
אתה אמור למצוא משהו דומה להלן בקובץ הזה
נכשל: Test Suite ''ns3-wifi-propagation-loss-models'' (ממש 0.02 משתמש 0.01 מערכת 0.00)
PASS: מקרה מבחן "בדוק ... Friis ... דגם ..." (אמיתי 0.01 משתמש 0.00 מערכת 0.00)
נכשל: מקרה מבחן "בדוק ... מרחק יומן ... דגם" (משתמש אמיתי 0.01 0.01 מערכת 0.00)
פרטים:
הודעה: קיבל ערך SNR בלתי צפוי
מצב: [תיאור ארוך של מה שבאמת נכשל]
בפועל: 176.395
הגבלה: 176.407 +- 0.0005
קובץ: ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
קו: 360
שימו לב שחבילת הבדיקה מורכבת משני מקרי בדיקה. מקרה המבחן הראשון בדק את
מודל אובדן ריבוי פריס ועבר. מקרה הבדיקה השני נכשל בבדיקת היומן
מודל התפשטות למרחקים. במקרה זה, נמצא SNR של 176.395, והבדיקה
צפי לערך של 176.407 נכון לשלושה מקומות עשרוניים. הקובץ שיישם
המבחן הנכשל מופיע ברשימה וכן שורת הקוד שהפעילה את הכשל.
אם תרצה, באותה קלות יכולת לכתוב קובץ HTML באמצעות ה --html אוֹפְּצִיָה
כמתואר לעיל.
בדרך כלל משתמש יפעיל את כל הבדיקות לפחות פעם אחת לאחר ההורדה ns-3 להבטיח ש
הסביבה שלו נבנתה בצורה נכונה ומניבה תוצאות נכונות
לפי חבילות הבדיקה. מפתחים יפעילו בדרך כלל את חבילות הבדיקה לפני ו
לאחר ביצוע שינוי כדי להבטיח שהם לא הציגו רגרסיה עם שלהם
שינויים. במקרה זה, ייתכן שהמפתחים לא ירצו להריץ את כל הבדיקות, אלא רק תת-קבוצה. ל
לדוגמה, ייתכן שהמפתח ירצה רק להריץ את בדיקות היחידה מעת לעת תוך כדי ביצוע
שינויים במאגר. במקרה הזה, test.py ניתן לומר להגביל את סוגי
מבחנים המופעלים למחלקה מסוימת של מבחנים. הפקודה הבאה תביא רק
בדיקות היחידה המופעלות:
$ ./test.py --constrain=unit
באופן דומה, הפקודה הבאה תגרום לכך שרק בדיקות העשן לדוגמה יופעלו:
$ ./test.py --constrain=unit
כדי לראות רשימה מהירה של הסוגים המשפטיים של אילוצים, אתה יכול לבקש אותם.
הפקודה הבאה
$ ./test.py --kinds
יביא לכך שהרשימה הבאה תוצג:
Waf: כניסה לספרייה `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: עוזב את הספרייה `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' הסתיים בהצלחה (0.939s)Waf: כניסה לספרייה `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
bvt: בדיקות אימות בנייה (כדי לראות אם הבנייה הושלמה בהצלחה)
ליבה: הפעל את כל הבדיקות מבוססות TestSuite (אל תכלול דוגמאות)
דוגמה: דוגמאות (כדי לראות אם תוכניות לדוגמה פועלות בהצלחה)
ביצועים: מבחני ביצועים (בדוק אם המערכת מהירה כמצופה)
מערכת: בדיקות מערכת (מרחיב מודולים לבדיקת אינטגרציה של מודולים)
יחידה: בדיקות יחידה (בתוך מודולים לבדיקת פונקציונליות בסיסית)
ניתן לספק כל אחד מסוגי הבדיקות האלה כאילוץ באמצעות ה --אילוץ אוֹפְּצִיָה.
כדי לראות רשימה מהירה של כל חבילות הבדיקה הזמינות, אתה יכול לבקש מהן
ברשימה. הפקודה הבאה,
$ ./test.py --list
תגרום לכך שתוצג רשימה של חבילת הבדיקה, בדומה ל
Waf: כניסה לספרייה `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: עוזב את הספרייה `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'בנייה' הסתיים בהצלחה (0.939 שניות)
היסטוגרמה
ns3-wifi-interference
ns3-tcp-cwnd
יכולת פעולה הדדית של ns3-tcp
לִטעוֹם
מכשירים-רשת-להבה
devices-mesh-dot11s
מכשירים-רשת
...
אובייקט-שם-שירות
תתקשר בחזרה
תכונות
config
ערך גלובלי
שורת הפקודה
בסיסי-אקראי-מספר
אובייקט
ניתן לבחור כל אחת מהסוויטות המפורטות להפעלה בעצמה באמצעות ה --סְוִיטָה אפשרות כ
המוצג לעיל.
בדומה לחבילות בדיקה, אפשר להריץ תוכנית אחת לדוגמא C++ באמצעות ה- --דוגמא
אוֹפְּצִיָה. שימו לב שאין צורך לכלול את הנתיב היחסי של הדוגמה וזה
לקובצי ההפעלה שנבנו עבור דוגמאות C++ אין הרחבות. נכנסים
$ ./test.py --example=udp-echo
גורם לכך שהדוגמה היחידה מופעלת.
PASS: דוגמאות לדוגמא/udp/udp-echo
אתה יכול לציין את הספרייה שבה נבנה ns-3 באמצעות ה --שביל לבנות אפשרות כ
הבא.
$ ./test.py --buildpath=/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/debug --example=wifi-simple-adhoc
אפשר להריץ תוכנית אחת לדוגמה של Python באמצעות ה --pyexample אוֹפְּצִיָה. שימו לב שה-
יש לכלול נתיב יחסי עבור הדוגמה ושדוגמאות Python אכן זקוקות להן
הרחבות. נכנסים
$ ./test.py --pyexample=examples/tutorial/first.py
גורם לכך שהדוגמה היחידה מופעלת.
PASS: דוגמאות/tutorial/first.py לדוגמה
מכיוון שדוגמאות Python אינן בנויות, אינך צריך לציין את הספרייה שבה ns-3
נבנה כדי להפעיל אותם.
בדרך כלל כאשר מופעלות תוכניות לדוגמה, הן כותבות כמות גדולה של נתוני קבצי מעקב.
זה נשמר בדרך כלל בספריית הבסיס של ההפצה (למשל,
/home/user/ns-3-dev). מתי test.py מנהל דוגמה, זה באמת לא מודאג לחלוטין
עם קבצי המעקב. הוא רק רוצה לקבוע אם ניתן לבנות ולהפעיל את הדוגמה
ללא שגיאה. מכיוון שזה המקרה, קבצי המעקב נכתבים לתוך a
/tmp/unchecked-traces מַדרִיך. אם אתה מפעיל את הדוגמה לעיל, אתה אמור להיות מסוגל למצוא
המשויך udp-echo.tr ו udp-echo-n-1.pcap קבצים שם.
רשימת הדוגמאות הזמינות מוגדרת על ידי התוכן של ספריית ''דוגמאות'' ב
ההפצה. אם תבחר דוגמה לביצוע באמצעות ה --דוגמא אוֹפְּצִיָה,
test.py לא יעשה שום ניסיון להחליט אם הדוגמה הוגדרה או לא, זה
פשוט ינסה להפעיל אותו ולדווח על התוצאה של הניסיון.
מתי test.py פועל, כברירת מחדל זה יבטיח תחילה שהמערכת הייתה מלאה
בנוי. ניתן להביס זאת על ידי בחירה ב- --nowaf אוֹפְּצִיָה.
$ ./test.py --list --nowaf
יביא להצגת רשימה של חבילות הבדיקה שנבנו כעת, בדומה ל:
ns3-wifi-propagation-loss-models
ns3-tcp-cwnd
יכולת פעולה הדדית של ns3-tcp
pcap-file-object
אובייקט-שם-שירות
מחוללי-מספרים אקראיים
שימו לב להיעדר ה- ווף לבנות מסרים.
test.py תומך גם בהפעלת חבילות הבדיקה והדוגמאות תחת valgrind. ולגרינד הוא א
תוכנית גמישה לאיתור באגים ופרופיל קובצי הפעלה של לינוקס. כברירת מחדל, valgrind פועל
כלי הנקרא memcheck, המבצע מגוון של פונקציות בדיקת זיכרון, כולל
זיהוי גישה לזיכרון לא מאותחל, שימוש לרעה בזיכרון שהוקצה (הפצות כפולות,
גישה לאחר חינמי וכו') ואיתור דליפות זיכרון. ניתן לבחור זאת באמצעות
--לִטחוֹן אוֹפְּצִיָה.
$ ./test.py --grind
בזמן שהוא פועל, test.py והתוכניות שהיא מפעילה בעקיפין, מייצרות מספר רב של
קבצים זמניים. בדרך כלל, התוכן של קבצים אלה אינו מעניין, אולם בחלקם
במקרים שבהם זה יכול להיות שימושי (למטרות איתור באגים) לצפות בקבצים אלה. test.py מספק
--לִשְׁמוֹר אפשרות שתגרום לקבצים זמניים אלה להישמר לאחר ההרצה
הושלם. הקבצים נשמרים בספרייה בשם testpy-output תחת ספריית משנה
נקרא על פי הזמן האוניברסלי המתואם הנוכחי (הידוע גם כ-Greenwich Mean
זְמַן).
$ ./test.py --retain
לבסוף, test.py מספק --מִלוּלִי אפשרות שתדפיס כמויות גדולות של מידע
על ההתקדמות שלו. זה לא צפוי שזה יהיה מאוד שימושי אלא אם כן
שגיאה. במקרה זה, אתה יכול לקבל גישה לפלט הסטנדרטי ולשגיאה הסטנדרטית
דווח על ידי הפעלת חבילות בדיקה ודוגמאות. בחר מילולית בצורה הבאה:
$ ./test.py --verbose
ניתן לערבב את כל האפשרויות הללו. לדוגמה, להפעיל את כל הליבה ns-3
בדיקות חבילות תחת valgrind, במצב מילולי, תוך יצירת קובץ פלט HTML, אחד
הייתי עושה:
$ ./test.py --verbose --grind --constrain=core --html=results.html
TestTaxonomy
כפי שהוזכר לעיל, מבחנים מקובצים למספר סיווגים מוגדרים באופן רחב
לאפשר למשתמשים להריץ בדיקות באופן סלקטיבי כדי לתת מענה לסוגי הבדיקות השונים שצריכים
להתבצע.
· בניית בדיקות אימות
· בדיקות יחידה
· בדיקות מערכת
· דוגמאות
· מבחני ביצועים
BuildVerificationTests
אלו בדיקות פשוטות יחסית שנבנות יחד עם ההפצה ונעשה בהן שימוש
כדי לוודא שהמבנה די עובד. בדיקות היחידה הנוכחיות שלנו חיות ב-
קבצי מקור של הקוד שהם בודקים ומובנים במודולי ns-3; וכך מתאים ל
תיאור של BVTs. BVTs חיים באותו קוד מקור שמובנה בקוד ns-3.
הבדיקות הנוכחיות שלנו הן דוגמאות לסוג זה של בדיקות.
יחידה בדיקות
בדיקות יחידה הן בדיקות מעורבות יותר שנכנסות לפרטים כדי לוודא שקטע קוד
עובד כפי שפורסם בבידוד. אין באמת סיבה לבדיקה מסוג זה
מובנה במודול ns-3. מסתבר, למשל, שהיחידה בודקת את האובייקט
שירות שמות הם בערך באותו גודל של קוד שירות שם האובייקט עצמו. בדיקות יחידה
הן בדיקות שבודקות סיביות בודדות של פונקציונליות שאינן מובנות בקוד ns-3,
אבל חיים באותה ספרייה כמו הקוד שהוא בודק. יתכן שהבדיקות הללו
בדוק גם אינטגרציה של קבצי יישום מרובים במודול. הקובץ
src/core/test/names-test-suite.cc הוא דוגמה לסוג זה של בדיקה. הקובץ
src/network/test/pcap-file-test-suite.cc הוא דוגמה נוספת שמשתמשת ב-pcap טוב ידוע
קובץ כקובץ וקטור בדיקה. קובץ זה מאוחסן באופן מקומי בספריית src/network.
מערכת בדיקות
בדיקות מערכת הן אלו הכוללות יותר ממודול אחד במערכת. יש לנו המון
מבחנים מסוג זה פועלים במסגרת הרגרסיה הנוכחית שלנו, אבל הם בדרך כלל
דוגמאות עמוסות מדי. אנו מספקים מקום חדש לבדיקה מסוג זה בספרייה
src/test. הקובץ src/test/ns3tcp/ns3-interop-test-suite.cc הוא דוגמה מהסוג הזה
של מבחן. הוא משתמש ב- NSC TCP כדי לבדוק את יישום ns-3 TCP. לעתים קרובות יהיה מבחן
וקטורים הנדרשים לבדיקה מסוג זה, והם מאוחסנים בספרייה שבה
חיי מבחן. לדוגמה, ns3tcp-interop-response-vectors.pcap הוא קובץ המורכב מקובץ
מספר כותרות TCP המשמשות כתגובות הצפויות של ns-3 TCP הנבדק
לגירוי שנוצר על ידי NSC TCP המשמש כיישום ''טוב ידוע''.
דוגמאות
הדוגמאות נבדקות על ידי המסגרת כדי לוודא שהן בנו ויפעלו. שום דבר הוא
מסומן, וכרגע קבצי ה-pcap פשוט נמחקים לתוך / Tmp להיזרק. אם
הדוגמאות רצות (אל תקרוס) הן עוברות את מבחן העשן הזה.
ביצוע בדיקות
מבחני ביצועים הם אלה שמפעילים חלק מסוים של המערכת וקובעים
אם הבדיקות בוצעו עד לסיומן בזמן סביר.
ריצה בדיקות
בדיקות מתנהלות בדרך כלל באמצעות הרמה הגבוהה test.py תכנית. כדי לקבל רשימה של
אפשרויות שורת הפקודה הזמינות, הפעל test.py - עזרה
תוכנית המבחן test.py יריץ גם את הבדיקות וגם את הדוגמאות שנוספו להן
את הרשימה לבדוק. ההבדל בין בדיקות לדוגמאות הוא כדלקמן. מבחנים
בדקו בדרך כלל שפלט סימולציה או אירועים ספציפיים תואמים את ההתנהגות הצפויה.
לעומת זאת, הפלט של דוגמאות לא נבדק, ותוכנית הבדיקה רק בודקת את
מצב יציאה של התוכנית לדוגמה כדי לוודא שהיא פועלת ללא שגיאה.
בקצרה, כדי להפעיל את כל הבדיקות, ראשית יש להגדיר בדיקות במהלך שלב ההגדרה, וכן
גם (אופציונלי) דוגמאות אם יש לבדוק דוגמאות:
$ ./waf --configure --enable-examples --enable-tests
לאחר מכן, בנה ns-3, ולאחר שהוא נבנה, פשוט הפעל test.py. test.py -h יציג מספר
של אפשרויות תצורה שמשנות את ההתנהגות של test.py.
התכנית test.py מפעיל, עבור בדיקות ודוגמאות C++, תוכנית C++ ברמה נמוכה יותר הנקראת
רץ מבחן כדי להריץ את הבדיקות בפועל. כפי שנדון להלן, זה רץ מבחן יכול להיות
דרך מועילה לניפוי באגים בבדיקות.
Debugging בדיקות
איתור הבאגים של תוכניות הבדיקה יתבצע בצורה הטובה ביותר בהרצת ה-test-runner ברמה נמוכה
תכנית. רץ המבחן הוא הגשר מקוד Python גנרי אל ns-3 קוד. זה
כתוב ב-C++ ומשתמש בתהליך גילוי הבדיקה האוטומטי ב- ns-3 קוד למצוא ו
לאפשר ביצוע של כל הבדיקות השונות.
הסיבה העיקרית מדוע test.py אינו מתאים לניפוי באגים הוא שאסור לו
רישום כדי להיות מופעל באמצעות NS_LOG משתנה סביבתי כאשר test.py פועל. זֶה
ההגבלה אינה חלה על קובץ ההפעלה של מבחן הרץ. לפיכך, אם אתה רוצה לראות רישום
פלט מהבדיקות שלך, עליך להריץ אותם באמצעות ה-test-runner ישירות.
על מנת להפעיל את ה-test-runner, אתה מפעיל אותו כמו כל קובץ הפעלה אחר ns-3 -- באמצעות
WAF. כדי לקבל רשימה של אפשרויות זמינות, אתה יכול להקליד:
$ ./waf --הרץ "test-runner --help"
אתה אמור לראות משהו כמו הבא
Waf: כניסה לספרייה `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: עוזב את הספרייה `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'בנייה' הסתיים בהצלחה (0.353 שניות)
--assert: אמור לבדיקות segfault (כמו assert) אם זוהתה שגיאה
--basedir=dir: הגדר את ספריית הבסיס (היכן למצוא src) ל''dir''
--tempdir=dir: הגדר את הספרייה הזמנית (היכן למצוא קבצי נתונים) ל''dir''
--constrain=test-type: הגבלת בדיקות לחבילות בדיקה מסוג ''test-type''
--help: הדפס הודעה זו
--kinds: רשום את כל סוגי הבדיקות הזמינים
--list: רשום את כל חבילות הבדיקה (אופציונלי מוגבל על ידי סוג הבדיקה)
--out=שם קובץ: הגדר את קובץ הפלט של סטטוס הבדיקה ל''שם הקובץ''
--suite=suite-name: הפעל את חבילת הבדיקה בשם ''suite-name''
--verbose: הפעל הודעות בחבילות בדיקת הריצה
ישנם מספר דברים זמינים עבורך אשר יהיו מוכרים לך אם יש לך
הביט בי test.py. יש לצפות לכך מכיוון שהרץ המבחן הוא רק ממשק
בֵּין test.py ו ns-3. ייתכן שתבחין שחסרות כאן פקודות הקשורות לדוגמה.
זה בגלל שהדוגמאות ממש לא ns-3 בדיקות. test.py מפעיל אותם כאילו היו
להציג סביבת בדיקה מאוחדת, אבל הם באמת שונים לגמרי ולא
ניתן למצוא כאן.
האפשרות החדשה הראשונה שמופיעה כאן, אך לא ב-test.py היא --לִטעוֹן אוֹפְּצִיָה. זֶה
אפשרות שימושית בעת ניפוי באגים במקרה בדיקה כאשר פועל תחת באגים כמו gdb. כאשר
נבחר, אפשרות זו אומרת למקרה הבדיקה הבסיסי לגרום להפרת פילוח אם
זוהתה שגיאה. יש לזה תופעת לוואי נחמדה של גרימת עצירת ביצוע התוכנית
(פרוץ ל-debugger) כאשר מתגלה שגיאה. אם אתה משתמש ב-gdb, אתה יכול להשתמש
אפשרות זו משהו כמו,
מעטפת $ ./ואף
$ cd build/debug/utils
$ gdb מבחן רץ
$ run --suite=global-value --assert
אם לאחר מכן נמצאה שגיאה בחבילת הבדיקה הגלובלית, תיווצר תקלת סג
והמאתר באגים (ברמת המקור) יעצור ב- NS_TEST_ASSERT_MSG שזיהה את
שגיאה.
אפשרות חדשה נוספת שמופיעה כאן היא --basedir אוֹפְּצִיָה. מסתבר שחלק
ייתכן שבדיקות יצטרכו להפנות לספריית המקור של ns-3 הפצה כדי למצוא מקומי
נתונים, כך שתמיד נדרשת ספריית בסיס להפעלת בדיקה.
אם תפעיל בדיקה מ-test.py, תוכנית Python תספק את האפשרות basedir עבור
אתה. כדי להריץ את אחת הבדיקות ישירות מהרץ המבחן באמצעות WAF, אתה תצטרך ל
ציין את חבילת הבדיקה שתפעל יחד עם ספריית הבסיס. אז אתה יכול להשתמש בקליפה
ולעשות:
$ ./waf --הרץ "test-runner --basedir=`pwd` --suite=pcap-file-object"
שימו לב למרכאות ''אחורה'' ב- pwd פקודה.
אם אתה מפעיל את חבילת הבדיקה מתוך מאתר באגים, זה יכול להיות די כואב להיזכר
והקלד כל הזמן את הנתיב המוחלט של ספריית בסיס ההפצה. בגלל
זאת, אם תשמיט את ה-basedir, רץ המבחן ינסה למצוא אחד עבורך. זה
מתחיל בספריית העבודה הנוכחית ועולה בעץ הספריות ומחפש א
קובץ ספרייה עם קבצים בשם גִרְסָה ו רישיון. אם הוא מוצא אחד, הוא מניח זאת
חייב להיות המבוסס ומספק לך אותו.
מִבְחָן תפוקה
חבילות בדיקה רבות צריכות לכתוב קבצים זמניים (כגון קבצי pcap) בתהליך של
להפעיל את הבדיקות. לאחר מכן הבדיקות זקוקות לספרייה זמנית כדי לכתוב אליה. הפייתון
כלי הבדיקה (test.py) יספק קובץ זמני באופן אוטומטי, אך אם הוא פועל עצמאי
יש לספק את הספרייה הזמנית הזו. בדיוק כמו במקרה המבוסס שלהם, זה יכול להיות
מעצבן כל הזמן לספק א --tempdir, אז רץ המבחן יבין אחד
לצאת בשבילך אם לא תספק אחד. תחילה הוא מחפש משתני סביבה בשם Tmp
ו TEMP ומשתמש באלה. אם לא אחת Tmp ולא TEMP מוגדרים זה בוחר / Tmp. הקוד
ואז מדביק מזהה המציין מה יצר את הספרייה (ns-3) ואז את השעה
(hh.mm.ss) ואחריו מספר אקראי גדול. רץ המבחן יוצר ספרייה של זה
שם שישמש בתור הספרייה הזמנית. קבצים זמניים אז נכנסים לספרייה ש
ייקרא משהו כמו
/tmp/ns-3.10.25.37.61537845
הזמן מסופק כרמז כדי שתוכל לשחזר בקלות יחסית מה
נעשה שימוש בספרייה אם אתה צריך לחזור ולהסתכל על הקבצים שהוצבו בה
במדריך.
מחלקה נוספת של פלט היא פלט מבחן כמו עקבות pcap שנוצרות כדי להשוות אליהן
פלט התייחסות. תוכנית הבדיקה תמחק אותם בדרך כלל לאחר חבילות הבדיקה כולם
לָרוּץ. כדי להשבית את מחיקת פלט הבדיקה, הפעל test.py עם אפשרות "שמור":
$ ./test.py -r
ניתן למצוא פלט בדיקה ב- testpy-output/ במדריך.
דווח of מבחן כישלונות
כאשר אתה מפעיל חבילת בדיקה באמצעות ה-test-runner, היא תפעיל את הבדיקה בשקט כברירת מחדל.
האינדיקציה היחידה שתקבל שהמבחן עבר הוא היעדרות של הודעה
החל מ- WAF אומר שהתוכנית החזירה משהו אחר מלבד קוד יציאה אפס. להשיג
פלט כלשהו מהבדיקה, עליך לציין קובץ פלט שאליו הבדיקות יהיו
כתוב את סטטוס ה-XML שלהם באמצעות ה- --הַחוּצָה אוֹפְּצִיָה. אתה צריך להיות זהיר לפרש את
תוצאות כי חבילות הבדיקה יהיו לצרף תוצאות על קובץ זה. לְנַסוֹת,
$ ./waf --הרץ "test-runner --basedir=`pwd` --suite=pcap-file-object --out=myfile.xml"
אם תסתכל על הקובץ myfile.xml אתה צריך לראות משהו כמו,
pcap-file-object
בדוק כדי לראות ש-PcapFile::Open עם מצב ''w'' עובד
לַעֲבוֹר
אמיתי 0.00 משתמש 0.00 מערכת 0.00
בדוק כדי לראות ש-PcapFile::Open עם מצב ''r'' עובד
לַעֲבוֹר
אמיתי 0.00 משתמש 0.00 מערכת 0.00
בדוק כדי לראות ש-PcapFile::Open עם מצב ''a'' עובד
לַעֲבוֹר
אמיתי 0.00 משתמש 0.00 מערכת 0.00
בדוק ש-PcapFileHeader מנוהל כהלכה
לַעֲבוֹר
אמיתי 0.00 משתמש 0.00 מערכת 0.00
בדוק ש-PcapRecordHeader מנוהל כהלכה
לַעֲבוֹר
אמיתי 0.00 משתמש 0.00 מערכת 0.00
בדוק ש-PcapFile יכול לקרוא קובץ pcap ידוע כטוב
לַעֲבוֹר
אמיתי 0.00 משתמש 0.00 מערכת 0.00
לַעֲבוֹר
אמיתי 0.00 משתמש 0.00 מערכת 0.00
אם אתה מכיר את XML זה אמור להיות די מובן מאליו. זה גם לא א
קובץ XML מלא מכיוון שחבילות בדיקה מתוכננות כך שהפלט שלהן יצורף למאסטר
קובץ סטטוס XML כמתואר ב- test.py סָעִיף.
Debugging מבחן מערכת כישלונות
כדי לנפות באגים קריסות בדיקה, כגון
CRASH: TestSuite ns3-wifi-interference
אתה יכול לגשת לתוכנית רץ המבחן הבסיסית דרך gdb באופן הבא, ולאחר מכן לעבור את
ארגומנט "--basedir=`pwd`" להרצה (תוכל גם להעביר ארגומנטים אחרים לפי הצורך, אבל
basedir הוא המינימום הדרוש):
$ ./waf --command-template="gdb %s" --הרץ "test-runner"
Waf: נכנס לספרייה `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
Waf: עוזב את הספרייה `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
'בנייה' הסתיים בהצלחה (0.380 שניות)
GNU gdb 6.8-debian
זכויות יוצרים (C) 2008 Free Software Foundation, Inc.
L cense GPLv3+: GNU GPL גרסה 3 ואילךhttp://gnu.org/licenses/gpl.html>
זוהי תוכנה חינמית: אתה רשאי לשנות ולהפיץ אותה מחדש.
אין אחריות, במידה המותרת בחוק. הקלד "הצג העתקה"
ו"הראות אחריות "לפרטים.
GDB זה הוגדר כ-"x86_64-linux-gnu"...
(gdb) r --basedir=`pwd`
התחלת תוכנית: <..>/build/debug/utils/test-runner --basedir=`pwd`
[ניפוי באגים בשרשור באמצעות libthread_db מופעל]
טענה נכשלה. file=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size () && uid != 0"
...
הנה דוגמה נוספת כיצד להשתמש בוולגרינד כדי לנפות באגים בבעיית זיכרון כגון:
VALGR: TestSuite devices-mesh-dot11s-regression
$ ./waf --command-template="valgrind %s --basedir=`pwd` --suite=devices-mesh-dot11s-regression" --הרץ מבחן ראנר
כיתה TestRunner
קובצי ההפעלה שמריצים תוכניות בדיקה ייעודיות משתמשים במחלקה של TestRunner. הכיתה הזאת
מספק רישום ורישום בדיקה אוטומטיים, כמו גם דרך לבצע את
מבחנים פרטניים. חבילות בדיקה בודדות משתמשות בבנאים גלובליים של C++ כדי להוסיף את עצמם אליהם
אוסף של חבילות בדיקה המנוהלות על ידי רץ המבחן. רץ המבחן משמש לרשימה
כל הבדיקות הזמינות ולבחור בדיקה שתתבצע. זהו שיעור די פשוט
המספק שלוש שיטות סטטיות לספק או הוספה והשגת חבילות בדיקה ל- a
אוסף מבחנים. ראה את החמצן לשיעור ns3::TestRunner לקבלת פרטים.
מִבְחָן סוויטה
הכל ns-3 המבחנים מסווגים לסוויטות מבחן ומקרי מבחן. חבילת בדיקה היא א
אוסף של מקרי בדיקה שמפעילים לחלוטין סוג נתון של פונקציונליות. כפי ש
המתואר לעיל, ניתן לסווג חבילות בדיקה כ,
· בניית בדיקות אימות
· בדיקות יחידה
· בדיקות מערכת
· דוגמאות
· מבחני ביצועים
סיווג זה מיוצא ממחלקת TestSuite. השיעור הזה די פשוט,
קיים רק כמקום לייצא סוג זה ולצבירת מקרי בדיקה. ממשתמש
פרספקטיבה, כדי ליצור TestSuite חדש במערכת צריך רק להגדיר חדש
כיתה שיורשת מהכיתה חבילת בדיקות ולבצע את שתי התפקידים הללו.
הקוד הבא יגדיר מחלקה חדשה שניתן להפעיל על ידה test.py כמבחן ''יחידה''
עם שם התצוגה, שם-חבילת-הבדיקה שלי.
class MySuite: TestSuite ציבורי
{
פּוּמְבֵּי:
MyTestSuite ();
};
MyTestSuite::MyTestSuite ()
: TestSuite ("שם-חבילת-הבדיקה שלי", UNIT)
{
AddTestCase (MyTestCase חדש);
}
MyTestSuite myTestSuite;
כיתת הבסיס דואגת לכל הרישום והדיווח הנדרשים כדי להיות טובים
אזרח במסגרת המבחן.
מִבְחָן מקרה
מבחנים בודדים נוצרים באמצעות מחלקה TestCase. מודלים נפוצים לשימוש במבחן
מקרה כולל "מקרה בדיקה אחד לכל תכונה", ו"מקרה בדיקה אחד לכל שיטה". תערובות של
ניתן להשתמש בדגמים אלה.
על מנת ליצור מקרה מבחן חדש במערכת, כל מה שצריך לעשות הוא לרשת מה-
TestCase מחלקה בסיס, לעקוף את הבנאי כדי לתת שם למקרה הבדיקה ולעקוף
מה היא DoRun שיטה להפעלת הבדיקה.
class MyTestCase: TestCase ציבורי
{
MyTestCase ();
ריק וירטואלי DoRun (ריק);
};
MyTestCase::MyTestCase ()
: TestCase ("בדוק קצת פונקציונליות")
{
}
לבטל את
MyTestCase::DoRun (בטל)
{
NS_TEST_ASSERT_MSG_EQ (true, true, "הודעת כשל כלשהי");
}
תשתיות
ישנם מספר כלי עזר מסוגים שונים שגם הם חלק מהבדיקה
מִסגֶרֶת. דוגמאות כוללות קובץ pcap כללי שימושי לאחסון וקטורי בדיקה; א
מיכל גנרי שימושי לאחסון חולף של וקטורי בדיקה במהלך ביצוע הבדיקה; ו
כלים להפקת מצגות המבוססות על תוצאות בדיקת אימות ואימות.
כלי עזר אלה אינם מתועדים כאן, אך לדוגמה, אנא ראה כיצד ה-TCP בודק
נמצא ב src/test/ns3tcp/ השתמש בקבצי pcap ובפלט הפניה.
איך ל לכתוב בדיקות
המטרה העיקרית של פרויקט ns-3 היא לעזור למשתמשים לשפר את התוקף ו
אמינות התוצאות שלהם. ישנם אלמנטים רבים להשגת דגמים תקפים ו
סימולציות, ובדיקה היא מרכיב מרכזי. אם אתה תורם מודלים או דוגמאות
ns-3, ייתכן שתתבקש לתרום קוד בדיקה. ישמשו דגמים שאתה תורם
במשך שנים רבות על ידי אנשים אחרים, שכנראה אין להם מושג במבט ראשון אם
הדגם נכון. קוד הבדיקה שאתה כותב עבור הדגם שלך יעזור להימנע מהעתיד
רגרסיות בפלט ויסייעו למשתמשים עתידיים בהבנת האימות ו
גבולות התחולה של הדגמים שלך.
ישנן דרכים רבות לאמת את נכונות היישום של מודל. בזה
בסעיף, אנו מקווים לכסות כמה מקרים נפוצים שיכולים לשמש כמדריך לכתיבה חדשה
בדיקות.
מדגם חבילת בדיקות שלד
כאשר מתחילים מאפס (כלומר לא מוסיפים TestCase ל-TestSuite קיים), אלה
צריך להחליט על דברים מראש:
· איך תיקרא חבילת הבדיקה
· איזה סוג של בדיקה זה יהיה (Build Verification Test, Unit Test, System Test, או
מבחן ביצועים)
· היכן יתקיים קוד הבדיקה (במודול ns-3 קיים או בנפרד ב
src/test/ directory). תצטרך לערוך את קובץ ה-wscript בספרייה זו
הידור הקוד החדש שלך, אם זה קובץ חדש.
תוכנית שנקראת src/create-module.py מהווה נקודת התחלה טובה. תוכנית זו יכולה להיות
מופעל כגון create-module.py נתב עבור מודול חדש היפותטי שנקרא נתב. פַּעַם
אם תעשה זאת, תראה א נתב ספרייה, וכן א test/router-test-suite.cc חבילת בדיקות.
קובץ זה יכול להיות נקודת התחלה לבדיקה הראשונית שלך. זוהי חבילת בדיקות עובדת,
למרות שהבדיקות שבוצעו בפועל הן טריוויאליות. העתק אותו למבחן של המודול שלך
ספרייה, ובצע החלפה גלובלית של "נתב" בקובץ הזה עבור משהו שקשור
לדגם שברצונך לבדוק. אתה יכול גם לערוך דברים כגון תיאור יותר
שם מקרה מבחן.
אתה גם צריך להוסיף בלוק ל-wscript שלך כדי לגרום לבדיקה זו להידור:
module_test.source = [
'test/router-test-suite.cc',
]
לפני שאתה באמת מתחיל לגרום לזה לעשות דברים שימושיים, זה עשוי לעזור לנסות להפעיל את
שֶׁלֶד. ודא ש-ns-3 הוגדר עם האפשרות "--enable-tests".
נניח שחבילת הבדיקה החדשה שלך נקראת "נתב" כמו כאן:
RouterTestSuite::RouterTestSuite ()
: TestSuite ("נתב", UNIT)
נסה פקודה זו:
$ ./test.py -s נתב
יש להפיק פלט כמו להלן:
PASS: נתב TestSuite
1 מתוך 1 מבחנים עבר (1 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
ראה src/lte/test/test-lte-antenna.cc לדוגמא עבדה.
מִבְחָן פקודות מאקרו
ישנן מספר פקודות מאקרו זמינות לבדיקת פלט תוכנית בדיקה עם צפוי
תְפוּקָה. פקודות מאקרו אלו מוגדרות ב src/core/model/test.h.
קבוצת פקודות המאקרו העיקריות שבהן נעשה שימוש כוללת את הדברים הבאים:
NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
NS_TEST_ASSERT_MSG_NE(actual, limit, msg)
NS_TEST_ASSERT_MSG_LT(actual, limit, msg)
NS_TEST_ASSERT_MSG_GT(actual, limit, msg)
NS_TEST_ASSERT_MSG_EQ_TOL(actual, limit, tol, msg)
הטיעון הראשון ממשי הוא הערך הנבדק, הערך השני להגביל הוא הצפוי
value (או הערך שעליו יש לבדוק), והארגומנט האחרון msg היא הודעת השגיאה ל
להדפיס אם הבדיקה נכשלת.
ארבעת פקודות המאקרו הראשונות לעיל בודקות שוויון, אי-שוויון, קטן מ- או גדול מ-
בהתאמה. המאקרו החמישי לעיל בודק שוויון, אך בתוך סובלנות מסוימת.
גרסה זו שימושית בעת בדיקת מספרי נקודה צפה לשוויון מול מגבלה,
שבו ברצונך להימנע מכשל בבדיקה עקב שגיאות עיגול.
לבסוף, יש גרסאות של האמור לעיל שבהן מילת המפתח רכוש מוחלף על ידי לְצַפּוֹת.
גרסאות אלה תוכננו במיוחד לשימוש בשיטות (במיוחד התקשרות חוזרת) להחזרה
בָּטֵל. שמור את השימוש בהם להתקשרות חוזרת שבה אתה משתמש בתוכניות הבדיקה שלך; אחרת, השתמש
מה היא רכוש גרסאות.
איך ל להוסיף an דוגמה התוכנית ל מה היא מבחן מערכת
אפשר "מבחן עשן" שדוגמאות קומפלט ורוץ בהצלחה עד סיומו (ללא
דליפות זיכרון) באמצעות examples-to-run.py סקריפט הממוקם בספריית הבדיקה של המודול שלך.
בקצרה, על ידי הכללת מופע של קובץ זה בספריית הבדיקה שלך, אתה יכול לגרום ל-
רץ מבחן לביצוע הדוגמאות המפורטות. בדרך כלל עדיף לוודא שאתה
בחר דוגמאות בעלות זמני ריצה קצרים למדי כדי לא לבלוע את הבדיקות. לִרְאוֹת
הדוגמה ב src/lte/test/ במדריך.
בדיקות ל בוליאני תוצאות
בדיקות תוצאות מתי אקראי is מעורב
בדיקות תפוקה נתונים נגד a ידוע הפצה
מתן לא טריוויאלי קלט וקטורים of נתונים
אחסון ו התייחסות לא טריוויאלי תפוקה נתונים
מציג שֶׁלְךָ תפוקה מבחן נתונים
תמיכה
יוצרים a חדש ns-3 מודל
פרק זה עובר על תהליך העיצוב של א ns-3 דֶגֶם. במקרים רבים של מחקר,
המשתמשים לא יהיו מרוצים רק להתאים את הדגמים הקיימים, אבל אולי ירצו להרחיב את
ליבת הסימולטור באופן חדשני. נשתמש בדוגמה של הוספת ErrorModel ל-a
פשוט ns-3 קישור כדוגמה מעודדת כיצד ניתן לגשת לבעיה זו ו
להמשיך בתכנון ויישום.
הערה:
תיעוד
כאן אנו מתמקדים בתהליך של יצירת מודלים חדשים ומודולים חדשים, וחלק מה
בחירות עיצוב מעורבות. למען הבהירות, אנו דוחים את הדיון בנושא מכניקה
של תיעוד מודלים וקוד מקור ל- תיעוד פרק.
עיצוב גישה
שקול איך אתה רוצה שזה יעבוד; מה זה צריך לעשות. תחשוב על הדברים האלה:
· פונקציונליות: איזו פונקציונליות צריכה להיות לו? מהי תכונות או תצורה
נחשף למשתמש?
· שימוש חוזר: כמה צריכים אחרים להיות מסוגלים לעשות שימוש חוזר בעיצוב שלי? האם אני יכול לעשות שימוש חוזר בקוד מ
ns-2 להתחיל? כיצד משתמש משלב את המודל עם שאר מודל אחר
סימולציה?
· תלות: כיצד אוכל לצמצם את הצגת תלות חיצונית בקוד החדש שלי
כמה שיותר (כדי שיהיה יותר מודולרי)? למשל, האם עלי להימנע מכל
תלות ב-IPv4 אם אני רוצה שהוא ישמש גם את IPv6? האם עלי להימנע מכל תלות
ב-IP בכלל?
אל תהססו לפנות אל ns-3-משתמשים or מפתחי ns רשימה אם יש לך שאלות.
בפרט, חשוב לחשוב על ה-API הציבורי של הדגם החדש שלך ולבקש
מָשׁוֹב. זה גם עוזר ליידע אחרים על העבודה שלך במקרה שאתה מעוניין
משתפי פעולה.
דוגמא: ErrorModel
קיים מודל שגיאה ב ns-2. זה מאפשר להעביר מנות לאובייקט מצבי זה
קובע, בהתבסס על משתנה אקראי, אם החבילה פגומה. המתקשר יכול
ואז להחליט מה לעשות עם החבילה (לזרוק אותה וכו').
ה-API הראשי של מודל השגיאה הוא פונקציה להעביר אליה מנה, וערך ההחזרה של
פונקציה זו היא בוליאני שאומרת למתקשר אם התרחשה שחיתות כלשהי. הערה
שבהתאם למודל השגיאה, מאגר נתוני המנות עלול להיות פגום או לא.
בואו נקרא לפונקציה הזו "IsCorrupt()".
עד כה, בעיצוב שלנו, יש לנו:
Class ErrorModel
{
פּוּמְבֵּי:
/ **
* \מחזירה true אם יש לראות ב-Packet כשגיאה/פגומה
* \param pkt מנה להחלת מודל שגיאה
*/
bool IsCorrupt (Ptr pkt);
};
שימו לב שאנחנו לא מעבירים מצביע const, ובכך מאפשרים לפונקציה לשנות את
packet if IsCorrupt() מחזירה true. לא כל דגמי השגיאה אכן ישנו את החבילה;
יש לתעד אם מאגר נתוני המנה פגום או לא.
ייתכן שנרצה גם גרסאות מיוחדות של זה, כמו ב ns-2, אז למרות שזה לא ה
הבחירה העיצובית היחידה עבור פולימורפיזם, אנו מניחים שנעשה תת-מחלקה למחלקת בסיס
ErrorModel עבור כיתות מיוחדות, כגון RateErrorModel, ListErrorModel וכו', כגון
נעשה ב ns-2.
ייתכן שאתה חושב בשלב זה, "למה לא להפוך את IsCorrupt() לשיטה וירטואלית?". זה
גישה אחת; השני הוא להפוך את הציבור לא וירטואלי לתפקוד עקיף באמצעות א
פונקציה וירטואלית פרטית (זה ב-C++ ידוע בתור ניב הממשק הלא וירטואלי והוא
אומץ ב ns-3 מחלקה ErrorModel).
בשלב הבא, האם למכשיר הזה צריכה להיות תלות כלשהי ב-IP או בפרוטוקולים אחרים? אנחנו לא רוצים
כדי ליצור תלות בפרוטוקולי אינטרנט (מודל השגיאה צריך להיות ישים עבור
גם פרוטוקולים שאינם אינטרנט), אז נזכור זאת מאוחר יותר.
שיקול נוסף הוא כיצד אובייקטים יכללו את מודל השגיאה הזה. אנו מדמיינים לשים
מגדיר מפורש ביישומי NetDevice מסוימים, למשל.:
/ **
* צרף דגם ErrorModel ל-PointToPointNetDevice.
*
* ה-PointToPointNetDevice עשוי לכלול אופציונלי ErrorModel
* שרשרת קבלת החבילות.
*
* @ראה ErrorModel
* @param em Ptr ל-ErrorModel.
*/
void PointToPointNetDevice::SetReceiveErrorModel(Ptr em);
שוב, זו לא הבחירה היחידה שיש לנו (אפשר לצבור דגמי שגיאה להרבה
אובייקטים אחרים), אבל זה עונה על מקרה השימוש העיקרי שלנו, שהוא לאפשר למשתמש להכריח
שגיאות בשידורי מנות מוצלחים אחרת, ברמת NetDevice.
אחרי קצת חשיבה והתבוננות בקיים ns-2 קוד, הנה API לדוגמה של בסיס
מחלקה ותת מחלקה ראשונה שניתן לפרסם לבדיקה ראשונית:
Class ErrorModel
{
פּוּמְבֵּי:
ErrorModel ();
וירטואלי ~ErrorModel ();
bool IsCorrupt (Ptr pkt);
void איפוס (בטל);
void הפעל (בטל);
void השבת (בטל);
bool IsEnabled (void) const;
פרטי you
bool virtual DoCorrupt (Ptr pkt) = 0;
ריק וירטואלי DoReset (ריק) = 0;
};
enum ErrorUnit
{
EU_BIT,
EU_BYTE,
EU_PKT
};
// קבע אילו מנות שגויה בהתאם לבסיס
// התפלגות משתנים אקראית, שיעור שגיאה ויחידה לשיעור.
class RateErrorModel: Public ErrorModel
{
פּוּמְבֵּי:
RateErrorModel ();
וירטואלי ~RateErrorModel ();
enum ErrorUnit GetUnit (void) const;
void SetUnit (enum ErrorUnit error_unit);
כפול GetRate (בטל) const;
void SetRate (שיעור כפול);
void SetRandomVariable (const RandomVariable &ranvar);
פרטי you
bool virtual DoCorrupt (Ptr pkt);
ריק וירטואלי DoReset (ריק);
};
פיגומים
נניח שאתה מוכן להתחיל ליישם; יש לך תמונה די ברורה של
מה אתה רוצה לבנות, ואולי ביקשת ממנו סקירה ראשונית או הצעות
הרשימה. אחת הדרכים לגשת לשלב הבא (הטמעה) היא יצירת פיגומים ו
מלא את הפרטים כשהעיצוב מתבגר.
סעיף זה עובר על רבים מהשלבים שאתה צריך לשקול כדי להגדיר פיגומים, או
שלד לא פונקציונלי של מה שהמודל שלך יישם בסופו של דבר. בדרך כלל זה טוב
תרגל לא לחכות כדי לשלב את הפרטים האלה בסוף, אלא במקום להסתיר א
השלד של המודל שלך לתוך המערכת מוקדם ולאחר מכן להוסיף פונקציות מאוחר יותר לאחר ה-API ו
נראה שהשילוב נכון.
שים לב שתרצה לשנות כמה דברים במצגת למטה עבור הדגם שלך
מכיוון שאם תעקוב אחר מודל השגיאה מילה במילה, הקוד שתפיק יתנגש ב-
מודל שגיאה קיים. להלן רק מתאר כיצד נבנה ErrorModel שאתה
יכול להתאים לדגמים אחרים.
סקירה מה היא ns-3 סִמוּל סִגְנוֹן מסמך
בשלב זה, ייתכן שתרצה לעצור ולקרוא את ns-3 מסמך בסגנון קידוד, במיוחד
אם אתה שוקל לתרום את הקוד שלך בחזרה לפרויקט. סגנון הקידוד
המסמך מקושר מדף הפרויקט הראשי: ns-3 קידוד סגנון.
להחליט איפה in מה היא מָקוֹר עֵץ מה היא מספר סימוכין צריך לִשְׁכּוֹן
כל ns-3 קוד המקור של הדגם נמצא בספרייה src /. תצטרך לבחור איזה
ספריית המשנה שבה הוא שוכן. אם זה קוד דגם חדש מסוג כלשהו, הגיוני לשים אותו
אל src / ספרייה איפשהו, במיוחד כדי להקל על האינטגרציה עם ה-build
מערכת.
במקרה של מודל השגיאה, זה קשור מאוד למחלקת החבילות, אז זה הגיוני
ליישם זאת ב src/network/ מודול איפה ns-3 מנות מיושמות.
WAF ו wscript
ns-3 משתמש ווף לבנות מערכת. תרצה לשלב את החדש שלך ns-3 משתמש ב-Waf
לבנות מערכת. תרצה לשלב את קבצי המקור החדשים שלך במערכת זו. זֶה
דורש שתוסיף את הקבצים שלך ל- wscript קובץ שנמצא בכל ספרייה.
נתחיל עם קבצים ריקים error-model.h ו- error-model.cc, ונוסיף את זה
src/network/wscript. זה באמת רק עניין של הוספת קובץ .cc לשאר הקובץ
קבצי מקור, וקובץ .h לרשימת קובצי הכותרות.
כעת, צץ לספרייה ברמה העליונה והקלד "./test.py". לא היית צריך להישבר
כל דבר במבצע הזה.
לכלול שומרים
הבא, בואו נוסיף כמה לכלול שומרים בקובץ הכותרות שלנו.:
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
...
#endif
מרחב שמות Ns3
ns-3 משתמש ns-3 מרחב שמות לבודד את הסמלים שלו ממרחבי שמות אחרים. בדרך כלל, א
המשתמש ישים בשלב הבא an ns-3 בלוק מרחב השמות גם בקובץ cc וגם ב-h.:
מרחב שמות ns3 {
...
}
בשלב זה, יש לנו כמה קבצי שלד שבהם נוכל להתחיל להגדיר את המחלקות החדשות שלנו.
קובץ הכותרת נראה כך:
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
מרחב שמות ns3 {
} // מרחב שמות ns3
#endif
תוך error-model.cc הקובץ פשוט נראה כך:
#include "error-model.h"
מרחב שמות ns3 {
} // מרחב שמות ns3
קבצים אלה צריכים להדר מכיוון שאין להם באמת תוכן. אנחנו מוכנים עכשיו
להתחיל להוסיף כיתות.
התחיל יישום
בשלב זה, אנחנו עדיין עובדים על כמה פיגומים, אבל אנחנו יכולים להתחיל להגדיר את שלנו
מחלקות, עם הפונקציונליות שתתווסף מאוחר יותר.
ירושה החל מ- מה היא חפץ מעמד?
זהו שלב עיצובי חשוב; האם להשתמש בכיתה חפץ כמחלקה בסיס לחדש שלך
שיעורים.
כפי שמתואר בפרק על ns-3 מודל-אובייקט, מחלקות שיורשות מכיתה
חפץ קבל נכסים מיוחדים:
· ה ns-3 סוג ומערכת תכונות (ראה תכונות)
· מערכת צבירת אובייקטים
· מערכת ספירת התייחסות מצביע חכמה (מחלקה Ptr)
שיעורים הנובעים מהשיעור ObjectBase} קבל את שני המאפיינים הראשונים למעלה, אבל לא
לקבל עצות חכמות. שיעורים הנובעים מהשיעור RefCountBase קבל רק את המצביע החכם
מערכת ספירת הפניות.
בפועל, שיעור חפץ הוא הגרסה של השלושה לעיל כי ns-3 המפתח ירצה
המפגש הנפוץ ביותר.
במקרה שלנו, אנחנו רוצים לעשות שימוש במערכת התכונות, ואנו נעביר מופעים
של אובייקט זה על פני ה ns-3 API ציבורי, אז מחלקה חפץ מתאים לנו.
התחיל חוגים
אחת הדרכים להמשיך היא להתחיל בהגדרת הפונקציות המינימליות ולראות אם הן יצליחו
לְלַקֵט. הבה נסקור מה כל מה שנדרש כדי ליישם כאשר אנו מפיקים מ-class Object.:
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
#include "ns3/object.h"
מרחב שמות ns3 {
class ErrorModel: אובייקט ציבורי
{
פּוּמְבֵּי:
סטטי TypeId GetTypeId (ריק);
ErrorModel ();
וירטואלי ~ErrorModel ();
};
class RateErrorModel: Public ErrorModel
{
פּוּמְבֵּי:
סטטי TypeId GetTypeId (ריק);
RateErrorModel ();
וירטואלי ~RateErrorModel ();
};
#endif
יש לציין כאן כמה דברים. אנחנו צריכים לכלול חפץ.ה. האמנה ב ns-3 האם זה אם
קובץ הכותרת ממוקם בשיתוף באותה ספרייה, הוא עשוי להיכלל ללא כל נתיב
קידומת. לכן, אם היינו מיישמים את ErrorModel ב src/core/model מדריך, אנחנו
יכול היה פשוט להגיד "#include "object.h"". אבל אנחנו בפנים src/network/model, אז אנחנו חייבים
כלול את זה בתור "#include "ns3/object.h"". שימו לב גם שזה יוצא מחוץ למרחב השמות
הצהרה.
שנית, כל מחלקה חייבת ליישם פונקציית חבר ציבורי סטטית שנקראת GetTypeId (בָּטֵל).
שלישית, זה רעיון טוב ליישם בנאים והרסים במקום לתת את
מהדר ליצור אותם, ולהפוך את ההורס וירטואלי. ב-C++, שים לב גם לעותק הזה
אופרטור ההקצאה ובני העתקה נוצרים אוטומטית אם הם לא מוגדרים, אז
אם אתה לא רוצה אותם, אתה צריך ליישם אותם כחברים פרטיים. היבט זה של
C++ נדון בספרו של Scott Meyers Effective C++. פריט 45.
הבה נסתכל כעת על קוד יישום שלד תואם בקובץ .cc.:
#include "error-model.h"
מרחב שמות ns3 {
NS_OBJECT_ENSURE_REGISTERED (ErrorModel);
TypeId ErrorModel::GetTypeId (בטל)
{
static TypeId tid = TypeId ("ns3::ErrorModel")
.SetParent ()
;
לחזור tid;
}
ErrorModel::ErrorModel ()
{
}
ErrorModel::~ErrorModel ()
{
}
NS_OBJECT_ENSURE_REGISTERED (RateErrorModel);
TypeId RateErrorModel::GetTypeId (בטל)
{
static TypeId tid = TypeId ("ns3::RateErrorModel")
.SetParent ()
.AddConstructor ()
;
לחזור tid;
}
RateErrorModel::RateErrorModel ()
{
}
RateErrorModel::~RateErrorModel ()
{
}
מהו GetTypeId (בָּטֵל) פוּנקצִיָה? הפונקציה הזו עושה כמה דברים. זה רושם א
מחרוזת ייחודית למערכת TypeId. זה קובע את ההיררכיה של אובייקטים ב
מערכת תכונות (via סטנדרנט). הוא גם מצהיר שניתן ליצור אובייקטים מסוימים באמצעות
מסגרת יצירת האובייקט (AddConstructor).
המאקרו NS_OBJECT_ENSURE_REGISTERED (שם הכיתה) יש צורך גם פעם אחת עבור כל שיעור כי
מגדיר שיטה חדשה של GetTypeId, והיא מבצעת את הרישום בפועל של המחלקה ל-
מערכת. הפרק מודל אובייקט דן בכך ביתר פירוט.
כולל חיצוני קבצים
רישום תמיכה
כאן, לכתוב a קצת על מוסיף |ns3| רישום פקודות מאקרו. הערות זֶה LOG_COMPONENT_DEFINE is
עשה בחוץ מה היא מרחב שמות Ns3
בַּנַאִי, ריק פונקציה אב טיפוס
מפתח משתנים (בְּרִירַת מֶחדָל ערכים, תכונות)
מִבְחָן תָכְנִית 1
חפץ מסגרת
מוסיף a מדגם תסריט
בשלב זה, אולי כדאי לנסות לקחת את הפיגום הבסיסי שהוגדר לעיל ולהוסיף אותו
לתוך המערכת. ביצוע שלב זה מאפשר כעת להשתמש בדגם פשוט יותר בעת הצנרת
לתוך המערכת ועשויים גם לחשוף אם יש צורך בשינויי עיצוב או API
עָשׂוּי. ברגע שזה נעשה, נחזור לבנות את הפונקציונליות של
ErrorModels עצמם.
להוסיף בסיסי תמיכה in מה היא כיתה
/* point-to-point-net-device.h */
class ErrorModel;
/ **
* מודל שגיאה עבור אירועי קבלת מנות
*/
Ptr m_receiveErrorModel;
להוסיף אביזר
לבטל את
PointToPointNetDevice::SetReceiveErrorModel (Ptr em)
{
NS_LOG_FUNCTION (זה << em);
m_receiveErrorModel = em;
}
.AddAttribute ("ReceiveErrorModel",
"מודל שגיאת המקלט המשמש לדמות אובדן מנות",
PointerValue (),
MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
MakePointerChecker ())
אֲנָך לתוך מה היא מערכת
void PointToPointNetDevice::Receive (Ptr חֲבִילָה)
{
NS_LOG_FUNCTION (חבילה << זו);
uint16_t protocol = 0;
if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (packet) )
{
//
// אם יש לנו מודל שגיאה והוא מציין שהגיע הזמן לאבד א
// חבילה פגומה, אל תעביר את החבילה הזו למעלה, עזוב אותה.
//
m_dropTrace (מנות);
}
אחר
{
//
// הכה על וו מעקב קבלת, הסר את כותרת הפרוטוקול מנקודה לנקודה
// והעבר את החבילה הזו למחסנית הפרוטוקול.
//
m_rxTrace (מנה);
ProcessHeader(מנה, פרוטוקול);
m_rxCallback (זה, חבילה, פרוטוקול, GetRemote ());
if (!m_promiscCallback.IsNull ())
{ m_promiscCallback (זה, מנה, פרוטוקול, GetRemote (),
GetAddress (), NetDevice::PACKET_HOST);
}
}
}
צור Null פוּנקצִיוֹנָלִי תסריט
/* simple-error-model.cc */
// מודל שגיאה
// אנו רוצים להוסיף מודל שגיאה ל-NetDevice של צומת 3
// אנו יכולים להשיג אחיזה ל-NetDevice דרך הערוץ והצומת
// מצביעים
Ptr nd3 = PointToPointTopology::GetNetDevice
(n3, ערוץ2);
Ptr em = צור ();
nd3->SetReceiveErrorModel (em);
bool
ErrorModel::DoCorrupt (Packet&p)
{
NS_LOG_FUNCTION;
NS_LOG_UNCOND("מושחת!");
return false;
}
בשלב זה, אנו יכולים להפעיל את התוכנית כאשר מודל השגיאה הטריוויאלי שלנו מחובר לקבל
נתיב של PointToPointNetDevice. הוא מדפיס את המחרוזת "מושחת!" עבור כל חבילה
התקבל בצומת n3. לאחר מכן, נחזור למודל השגיאה כדי להוסיף תת מחלקה שמבצעת
מודל שגיאות מעניין יותר.
להוסיף a תת מחלקה
מחלקת הבסיס הטריוויאלית ErrorModel לא עושה שום דבר מעניין, אבל היא מספקת א
ממשק בסיס שימושי (Corrupt () ו-Reset ()), מועבר לפונקציות וירטואליות
ניתן לסווג משנה. בשלב הבא נשקול את מה שאנו מכנים BasicErrorModel שמבוסס עליו
מה היא ns-2 ErrorModel class (in ns-2/queue/errmodel.{cc,h}).
אילו מאפיינים אנחנו רוצים שיהיו לזה, מנקודת מבט של ממשק משתמש? אנחנו נשמח
כדי שהמשתמש יוכל להחליף באופן טריוויאלי את סוג ה-ErrorModel המשמש ב-
NetDevice. נרצה גם את היכולת להגדיר פרמטרים הניתנים להגדרה.
להלן מספר דרישות פשוטות שאנו נשקול:
· יכולת להגדיר את המשתנה האקראי השולט בהפסדים (ברירת המחדל היא UniformVariable)
· יכולת להגדיר את יחידת הפירוט (סיביות, בתים, מנות, זמן) של הפירוט שעליה יש שגיאות
הוחל.
· יכולת להגדיר את שיעור השגיאות (למשל 10^-3) התואם ליחידה הנ"ל של
פירוט.
· יכולת הפעלה/השבתה (ברירת המחדל מופעלת)
איך ל תת מחלקה
אנו מכריזים על BasicErrorModel כ-subclass של ErrorModel באופן הבא:
class BasicErrorModel: Public ErrorModel
{
פּוּמְבֵּי:
סטטי TypeId GetTypeId (ריק);
...
פרטי you
// הטמעת פונקציות וירטואליות טהורות במחלקת בסיס
bool virtual DoCorrupt (Ptr p);
bool וירטואלי DoReset (ריק);
...
}
ולהגדיר את פונקציית המשנה GetTypeId על ידי הגדרת מחרוזת TypeId ייחודית ו
הגדרת ההורה ל-ErrorModel:
TypeId RateErrorModel::GetTypeId (בטל)
{
static TypeId tid = TypeId ("ns3::RateErrorModel")
.SetParent ()
.AddConstructor ()
...
לִבנוֹת ליבה פונקציות ו יחידה בדיקות
תביעה מאקרו
כתיבה יחידה בדיקות
מוסיף a חדש מודול ל ns-3
כאשר יצרת קבוצה של שיעורים, דוגמאות ומבחנים קשורים, הם יכולים להיות
משולבים יחד ל- an ns-3 מודול כך שניתן יהיה להשתמש בהם עם הקיים ns-3 מודולים
ועל ידי חוקרים אחרים.
פרק זה מנחה אותך בשלבים הדרושים להוספת מודול חדש ns-3.
שלב 0 - מודול מערך
ניתן למצוא את כל המודולים ב- src מַדרִיך. כל מודול ניתן למצוא בספרייה
בעל אותו שם כמו המודול. לדוגמה, ה ספקטרום מודול ניתן למצוא כאן:
src/spectrum. אנחנו נצטט מה- ספקטרום מודול להמחשה.
למודול אב טיפוסי יש את מבנה הספריות הבא ואת הקבצים הדרושים:
src /
שם מודול/
כריכות/
דוק/
דוגמאות/
wscript
עוֹזֵר/
דֶגֶם/
מבחן /
examples-to-run.py
wscript
לא כל הספריות יהיו נוכחות בכל מודול.
שלב 1 - צור a מודול שֶׁלֶד
תוכנית python מסופקת בספריית המקור שתיצור שלד עבור חדש
מודול. למטרות הדיון הזה נניח שהמודול החדש שלך נקרא
מודול חדש. מ ה src ספרייה, בצע את הפעולות הבאות כדי ליצור את המודול החדש:
$ ./create-module.py new-module
בשלב הבא, cd אל תוך מודול חדש; תמצא את פריסת הספרייה הזו:
$ cd מודול חדש
ש"ס
דוגמאות למסמך עוזר מודל test wscript
ביתר פירוט, ה create-module.py התסריט ייצור את המדריכים וגם את ההתחלה
שלד wscript, .h, . DC ו .ראשון קבצים. המודול השלם עם קבצי השלד נראה
ככה:
src /
new-module/
דוק/
new-module.rst
דוגמאות/
new-module-example.cc
wscript
עוֹזֵר/
new-module-helper.cc
new-module-helper.h
דֶגֶם/
new-module.cc
new-module.h
מבחן /
new-module-test-suite.cc
wscript
(אם נדרש ה כריכות/ ספרייה הרשומה ב שלב 0 ייווצר אוטומטית במהלך
המבנה.)
בהמשך נעבור על אופן התאמה אישית של מודול זה. הַלשָׁנָה WAF על הקבצים אשר
להרכיב את המודול שלך נעשה על ידי עריכת השניים wscript קבצים. נלך דרך ה
השלבים העיקריים בפרק זה.
הכל ns-3 המודולים תלויים ב הליבה מודול ובדרך כלל על מודולים אחרים. התלות הזו
מצוין ב- wscript קובץ (ברמה העליונה של המודול, לא הנפרד wscript
הקובץ דוגמאות מַדרִיך!). בשלד wscript השיחה שתכריז על שלך
מודול חדש ל WAF ייראה כך (לפני עריכה):
def build (bld):
module = bld.create_ns3_module('new-module', ['core'])
בואו נניח את זה מודול חדש תלוי ב אינטרנט, ניידות, ו aodv מודולים. לאחר
לערוך את זה wscript הקובץ צריך להיראות כך:
def build (bld):
module = bld.create_ns3_module('new-module', ['internet', 'mobility', 'aodv'])
שימו לב שיש לרשום רק תלות של מודול ברמה הראשונה, וזו הסיבה שהסרנו
הליבה; ה אינטרנט מודול בתורו תלוי הליבה.
סביר להניח למודול שלך יהיו קובצי מקור של מודל. שלדים ראשוניים (אשר יהיו
הידור בהצלחה) נוצרו ב model/new-module.cc ו model/new-module.h.
אם למודול שלך יהיו קבצי מקור מסייעים, הם ייכנסו ל- עוֹזֵר/
מַדרִיך; שוב, שלדים ראשוניים נוצרים בספרייה הזו.
לבסוף, כדאי לכתוב מבחנים ודוגמאות. אלה יהיו כמעט בוודאות
נדרש כדי שהמודולים החדשים יתקבלו לרשמי ns-3 עץ מקור. שלד
חבילת בדיקה ומקרה בדיקה נוצרים ב- מבחן / מַדרִיך. חבילת בדיקות השלד תעשה זאת
מכילים את הבנאי שלהלן, המכריז על בדיקת יחידה חדשה בשם מודול חדש, עם
מקרה מבחן יחיד המורכב מהכיתה NewModuleTestCase1:
NewModuleTestSuite::NewModuleTestSuite ()
: TestSuite ("מודול חדש", UNIT)
{
AddTestCase (NewModuleTestCase1 חדש);
}
שלב 3 - מכריזים מָקוֹר קבצים
יש לציין את הכותרות הציבוריות וקובצי קוד המקור עבור המודול החדש שלך ב-
wscript הקובץ על ידי שינויו עם עורך הטקסט שלך.
כדוגמה, לאחר ההכרזה על ספקטרום מודול, src/spectrum/wscript מציין את
קובצי קוד מקור עם הרשימה הבאה:
def build (bld):
module = bld.create_ns3_module('spectrum', ['internet', 'propagation', 'antenna', 'applications'])
module.source = [
'model/spectrum-model.cc',
'model/spectrum-value.cc',
.
.
.
'model/microwave-oven-spectrum-value-helper.cc',
'helper/spectrum-helper.cc',
'helper/adhoc-aloha-noack-ideal-phy-helper.cc',
'helper/waveform-generator-helper.cc',
'helper/spectrum-analyzer-helper.cc',
]
האובייקטים הנובעים מהידור של מקורות אלה יורכבו לתוך ספריית קישורים,
אשר יהיה מקושר לכל תוכנה המסתמכת על מודול זה.
אבל איך תוכניות כאלה לומדות את ה-API הציבורי של המודול החדש שלנו? תמשיך לקרוא!
שלב 4 - מכריזים הציבור כותרת קבצים
גם קובצי הכותרות המגדירים את ה-API הציבורי של המודל והעוזרים שלך צריכים להיות
שצוין ב wscript קובץ.
ממשיכים עם ספקטרום איור דגם, קובצי הכותרות הציבוריים מפורטים
עם הבית הבא. (שים לב שהטיעון ל- בלד הפונקציה מספרת WAF ל
התקן את הכותרות של מודול זה עם השני ns-3 כותרות):
headers = bld(features='ns3header')
headers.module = 'ספקטרום'
headers.source = [
'model/spectrum-model.h',
'model/spectrum-value.h',
.
.
.
'model/microwave-oven-spectrum-value-helper.h',
'helper/spectrum-helper.h',
'helper/adhoc-aloha-noack-ideal-phy-helper.h',
'helper/waveform-generator-helper.h',
'helper/spectrum-analyzer-helper.h',
]
כותרות המפורסמות באופן זה יהיו נגישות למשתמשי הדגם שלך עם include
אמירות כמו
#include "ns3/spectrum-model.h"
אין לכלול כאן כותרות המשמשות באופן פנימי בהחלט ביישום שלך. הֵם
עדיין נגישים ליישום שלך על ידי כלול הצהרות כמו
#include "my-module-implementation.h"
שלב 5 - מכריזים בדיקות
אם במודול החדש שלך יש מבחנים, יש לציין אותם ברשימה שלך wscript הקובץ מאת
לשנות אותו עם עורך הטקסט שלך.
השמיים ספקטרום בדיקות הדגם מוגדרות עם הבית הבא:
module_test = bld.create_ns3_module_test_library('spectrum')
module_test.source = [
'test/spectrum-interference-test.cc',
'test/spectrum-value-test.cc',
]
לִרְאוֹת בדיקות למידע נוסף על איך לכתוב מקרי מבחן.
שלב 6 - מכריזים דוגמאות
אם למודול החדש שלך יש דוגמאות, יש לציין אותן ברשימה שלך דוגמאות/wscript
קוֹבֶץ. (השלד ברמה העליונה wscript יכלול באופן רקורסיבי דוגמאות/wscript רק אם
הדוגמאות הופעלו בזמן ההגדרה.)
השמיים ספקטרום המודל מגדיר את הדוגמה הראשונה שלו src/spectrum/examples/wscript עם
def build (bld):
obj = bld.create_ns3_program('adhoc-aloha-ideal-phy',
['ספקטרום', 'ניידות'])
obj.source = 'adhoc-aloha-ideal-phy.cc'
שימו לב שהארגומנט השני לפונקציה create_ns3_program() היא רשימת המודולים
שהתוכנית שנוצרת תלויה בו; שוב, אל תשכח לכלול מודול חדש in
הרשימה. עדיף לרשום רק את התלות הישירה של המודול, ולאפשר WAF
להסיק את עץ התלות המלא.
מדי פעם, למען הבהירות, ייתכן שתרצה לפצל את היישום עבור הדוגמה שלך בין
מספר קבצי מקור. במקרה זה, פשוט כלול את הקבצים האלה כמפורש נוסף
מקורות הדוגמה:
obj = bld.create_ns3_program('new-module-example', [new-module])
obj.source = ['new-module-example.cc', 'new-module-example-part.cc']
דוגמאות לפייתון מצוינות באמצעות קריאת הפונקציה הבאה. שימו לב שהשני
ארגומנט עבור הפונקציה register_ns3_script() היא רשימת המודולים שה-Python
הדוגמה תלויה ב:
bld.register_ns3_script('new-module-example.py', ['new-module'])
שלב 7 - דוגמאות הפעלה as בדיקות
בנוסף להרצת קוד בדיקה מפורש, ניתן להכשיר את מסגרת הבדיקה גם
הפעל תוכניות דוגמא מלאות כדי לנסות לתפוס רגרסיות בדוגמאות. עם זאת, לא כולם
דוגמאות מתאימות למבחני רגרסיה. הקובץ test/examples-to-run.py שולט ב
הפעלת הדוגמאות כאשר מסגרת הבדיקה פועלת.
השמיים ספקטרום דוגמאות מודל המנוהלות על ידי test.py מצוינים ב
src/spectrum/test/examples-to-run.py באמצעות שתי הרשימות הבאות של C++ ו-Python
דוגמאות:
# רשימה של דוגמאות C++ להפעלה כדי להבטיח שהן יישארו
# ניתן לבנייה ולהפעלה לאורך זמן. כל טופלה ברשימה מכילה
#
# (example_name, do_run, do_valgrind_run).
#
# ראה test.py למידע נוסף.
cpp_examples = [
("adhoc-aloha-ideal-phy", "True", "True"),
("adhoc-aloha-ideal-phy-with-מיקרוגל-תנור", "True", "True"),
("adhoc-aloha-ideal-phy-matrix-propagation-loss model", "True", "True"),
]
# רשימה של דוגמאות Python להפעלה כדי להבטיח שהן יישארו
# ניתן להרצה לאורך זמן. כל טופלה ברשימה מכילה
#
# (example_name, do_run).
#
# ראה test.py למידע נוסף.
python_examples = [
("sample-simulator.py", "True"),
]
כפי שצוין בהערה, כל ערך ברשימת C++ של דוגמאות להרצה מכיל את
טיפל (example_name, do_run, do_valgrind_run), שם
· example_name הוא קובץ ההפעלה שיש להפעיל,
· do_run הוא תנאי להפעלת הדוגמה, ו
· do_valgrind_run הוא תנאי שבו ניתן להפעיל את הדוגמה תחת valgrind. (זֶה
נחוץ כי NSC גורם לקריסות הוראה לא חוקיות עם כמה בדיקות כאשר הם
מופעלים תחת ולגרינד.)
שימו לב ששני התנאים הם הצהרות Python שיכולות להיות תלויות בהן WAF תצורה
משתנים. לדוגמה,
("tcp-nsc-lfn", "NSC_ENABLED == נכון", "NSC_ENABLED == שקר"),
כל ערך ברשימת הדוגמאות של Python להפעלה מכיל את ה-tuple (example_name,
do_run), כאשר, באשר לדוגמאות C++,
· example_name הוא הסקריפט של Python שיש להפעיל, ו
· do_run הוא תנאי להפעלת הדוגמה.
שוב, התנאי הוא הצהרת Python שיכולה להיות תלויה בה WAF משתני תצורה.
לדוגמה,
("realtime-udp-echo.py", "ENABLE_REAL_TIME == False"),
שלב 8 - גדר ו לִבנוֹת
כעת תוכל להגדיר, לבנות ולבדוק את המודול שלך כרגיל. עליך להגדיר מחדש את
פרויקט כצעד ראשון כך WAF שומר את המידע החדש בקובץ שלך wscript קבצים, או
אחרת המודול החדש שלך לא ייכלל ב-build.
$ ./waf configure --enable-examples --enable-tests
$ ./waf build
$ ./test.py
חפש את חבילת הבדיקה של המודול החדש שלך (ותוכניות לדוגמה, אם יש למודול שלך
מופעל) בפלט הבדיקה.
שלב 9 - פיתון כריכות
הוספת כריכות Python למודול שלך היא אופציונלית, והשלב מוענק על ידי
ברירת המחדל ב- create-module.py תַסרִיט.
# bld.ns3_python_bindings()
אם אתה רוצה לכלול כריכות Python (דרוש רק אם אתה רוצה לכתוב Python ns-3
תוכניות במקום תוכניות C++ ns-3), עליך לבטל את ההערות לעיל ולהתקין את
מערכת סריקה של Python API (מכוסה במקום אחר במדריך זה) וסרוק את המודול שלך ל
ליצור כריכות חדשות.
יוצרים תיעוד
ns-3 מספק שני סוגים של תיעוד: פרקים בסגנון "מדריך למשתמש", ו
תיעוד קוד מקור API.
פרקי "מדריך למשתמש" נכתבים ביד ReStructuredText פורמט (.ראשון), שהוא
מעובד על ידי מערכת התיעוד Python ספינקס ליצירת דפי אינטרנט וקבצי PDF.
תיעוד ה-API נוצר מקוד המקור עצמו, באמצעות חמצן, לייצר
דפי אינטרנט מקושרים. שני אלה חשובים: פרקי הספינקס מסבירים את למה
וסקירה כללית של שימוש במודל; תיעוד ה-API מסביר את אֵיך פרטים.
פרק זה נותן סקירה מהירה של כלים אלה, תוך שימת דגש על שימוש מועדף ו
התאמות אישיות עבור ns-3.
כדי לבנות את כל התיעוד הסטנדרטי:
$ ./waf docs
לאפשרויות מיוחדות יותר, המשך לקרוא.
מתעד עם ספינקס
אנו משתמשים ספינקס ליצור פרקי הסבר המתארים את העיצוב והשימוש של כל אחד מהם
מודול. כרגע אתה קורא את תיעוד פֶּרֶק. ה להציג מָקוֹר קישור
סרגל הצד יראה לך את מקור reStructuredText עבור פרק זה.
מוסיף חדש פרקים
הוספת פרק חדש לוקחת שלושה שלבים (המתואר בפירוט רב יותר להלן):
1. לבחור איפה? תיקי התיעוד יפעלו.
2. קישור מדף קיים לתיעוד החדש.
3. הוסף את הקובץ החדש ל- קובץ Makefile.
איפה?
תיעוד עבור מודול ספציפי, Foo, אמור להיכנס בדרך כלל src/foo/doc/. לדוגמה
src/foo/doc/foo.rst יהיה המסמך ברמה העליונה של המודול. ה
src/create-module.py script ייצור עבורך את הקובץ הזה.
דגמים מסוימים דורשים כמה .ראשון קבצים, ודמויות; כל אלה צריכים להיכנס ל
src/foo/doc/ מַדרִיך. המסמכים למעשה בנויים על ידי ספינקס Makefile. עבור במיוחד
תיעוד מעורב, זה עשוי להיות מועיל לקבל מקומי קובץ Makefile ב src/foo/doc/
ספרייה כדי לפשט את בניית התיעוד עבור מודול זה (אַנטֶנָה היא דוגמא).
הגדרה זו אינה קשה במיוחד, אך היא מעבר לתחום של פרק זה.
במקרים מסוימים, התיעוד משתרע על פני מספר דגמים; ה רשת הפרק הוא דוגמה. ב
מקרים אלה מוסיפים את .ראשון קבצים ישירות אל doc/models/source/ עשוי להיות מתאים.
קישור
הספינקס חייב לדעת איפה הפרק החדש שלך אמור להופיע. ברוב המקרים, דגם חדש
הפרק צריך להופיע ב מודלים סֵפֶר. כדי להוסיף את הפרק שלך לשם, ערוך
doc/models/source/index.rst
.. toctree::
:maxdepth: 1
ארגון
הנפשה
אַנטֶנָה
aodv
יישומים
...
הוסף את שם המסמך שלך (ללא ה .ראשון הרחבה) לרשימה זו. נא לשמור את
דגם פרקים בסדר אלפביתי, כדי להקל על סריקה חזותית לפרקים ספציפיים.
קובץ Makefile
אתה גם צריך להוסיף את המסמך שלך למתאים קובץ Makefile, כך לעשות יודע לבדוק את זה
לעדכונים. ספר הדוגמניות Makefile הוא doc/models/Makefile, הספר המדריך Makefile הוא
doc/manual/Makefile.
# רשום את כל קבצי ה-.rst של ספריית הדגמים שיש להעתיק ל-$SOURCETEMP
מקורות = \
source/conf.py \
source/_static \
source/index.rst \
source/replace.txt \
source/organization.rst \
...
$(SRC)/antenna/doc/source/antenna.rst \
...
אתה מוסיף את שלך .ראשון קבצים ל מקורות מִשְׁתַנֶה. כדי להוסיף דמויות, קרא את ההערות ב-
קובץ Makefile כדי לראות איזה משתנה צריך להכיל את קובצי התמונה שלך. שוב, בבקשה שמור את אלה
בסדר אלפביתי.
בִּניָן ספינקס Docs
בניית תיעוד הספינקס היא די פשוטה. לבנות את כל הספינקס
תיעוד:
$ ./waf ספינקס
כדי לבנות רק את תיעוד המודלים:
$ make -C doc/models
כדי לראות את התיעוד שנוצר, פנה אליו בדפדפן שלך doc/models/build/html.
כפי שאתה יכול לראות, Sphinx משתמש ב-Make כדי להנחות את התהליך. יעד ברירת המחדל בונה הכל
טפסי פלט מופעלים, אשר ב ns-3 הם מרובי העמודים html, עמוד יחיד singlehtml, ו
pdf (שרף גומי). כדי לבנות רק את ה-html מרובה עמודים, אתה מוסיף את ה- html יַעַד:
$ make -C doc/models html
זה יכול להיות מועיל כדי להפחית את זמן הבנייה (ואת גודל הפטפוט של הבנייה) כפי שאתה
כותבים את הפרק שלך.
לפני העברת התיעוד שלך לריפו, אנא בדוק שהוא נבנה ללא
שגיאות או אזהרות. תהליך הבנייה מייצר הרבה פלט (בעיקר פטפוט רגיל
מ-LaTeX), מה שעלול להקשות לראות אם יש אזהרות ספינקס או
שגיאות. כדי למצוא אזהרות ושגיאות חשובות בנה רק את html גרסה, ואז חפש
יומן הבנייה עבור אזהרה or שגיאה.
ns-3 פרטים
הספינקס תיעוד ו הדרכה הם די טובים. לא נשכפל את היסודות
כאן, במקום להתמקד בשימוש מועדף עבור ns-3.
· התחל מסמכים בשתי השורות הבאות:
.. include:: replace.txt
.. הדגשה:: cpp
השורה הראשונה מאפשרת כמה החלפות פשוטות. למשל, הקלדה |ns3| מעבד כ
ns-3. השני מגדיר את ברירת המחדל של קוד המקור המדגיש את שפת ה-
קובץ, מכיוון שהניחוש של המנתח לא תמיד מדויק. (אפשר גם להגדיר את
שפה במפורש עבור גוש קוד בודד, ראה להלן.)
· מקטעים:
ספינקס די ליברלי לגבי סימון כותרות סעיפים. לפי המוסכמה, אנחנו מעדיפים את זה
הִיֵרַרכִיָה:
.. היררכיית כותרות:
------------- פרק
************* סעיף (#.#)
============= סעיף משנה (#.#.#)
############# תת תת-סעיף
· הדגשת תחביר:
כדי להשתמש במדגיש התחביר המוגדר כברירת מחדל, פשוט התחל בלוק קוד מקור:
┌──יתוח ───────────────────────────────┐
│מקור הספינקס │ פלט מעובד │
├──יתוח ───────────────────────────────┤
│ │ ה פרובניץ הגישה אליו: │
│ אל ``פרובניץ`` גישה:: │ │
│ │ פו::פרובניץ פרוב; │
│ Foo::פרובניץ פרוב; │ frob.Set (...); │
│ frob.Set (...); │ │
└──יתוח ────────────────────────────────
כדי להשתמש במדגיש תחביר ספציפי, למשל, לחבוט פקודות מעטפת:
┌──יתוח ───┐
│מקור הספינקס │ פלט מעובד │
├──יתוח ───┤
│ │ │
│ .. קוד מקור:: bash │ $ ls │
│ │ │
│ $ ls │ │
└──יתוח ───┘
· סימוני קיצור:
הקיצורים האלה מוגדרים:
┌────────────────────────┬──────────
│מקור הספינקס │ פלט מעובד │
├────────────────────────┼───────────────────
│ │ ns-3 │
│ |ns3| │ │
├────────────────────────┼───────────────────
│ │ ns-2 │
│ |ns2| │ │
├────────────────────────┼───────────────────
│ │ │
│ |בדוק| │ │
├────────────────────────┼───────────────────
│ │ RFC 6282 │
│ :rfc:`6282` │ │
└────────────────────────┴───────────────
מתעד עם חמצן
אנו משתמשים חמצן לייצר ניתן לעיון תיעוד API. Doxygen מספק מספר
תכונות שימושיות:
· טבלת סיכום של כל חברי הכיתה.
· גרפים של ירושה ושיתוף פעולה לכל הכיתות.
· קישורים לקוד המקור המיישמים כל פונקציה.
· קישורים לכל מקום שבו משתמש חבר.
· קישורים לכל אובייקט המשמש ביישום פונקציה.
· קיבוץ של מחלקות קשורות, כגון כל השיעורים הקשורים לפרוטוקול ספציפי.
בנוסף, אנו משתמשים ב- TypeId מערכת להוסיף לתיעוד עבור כל מחלקה
· Config שבילים שבהם ניתן להגיע לאובייקטים כאלה.
· תיעוד לכל תכונות, לרבות תכונות מוגדר בכיתות הורים.
· תיעוד לכל עקבות מקורות שהוגדרו על ידי הכיתה.
Doxygen פועלת על ידי סריקת קוד המקור, מחפשת הערות מסומנות במיוחד. זה
גם יוצר הפניה צולבת, המציינת איפה כל קובץ, מחלקה, שיטה ומשתנה הוא
מְשׁוּמָשׁ.
מועדף סִגְנוֹן
הסגנון המועדף להערות Doxygen הוא סגנון JavaDoc:
/ **
* תיאור קצר של מחלקה או שיטה זו.
* שורות סמוכות הופכות לפסקה בודדת.
*
* תיאור ארוך יותר, עם הרבה פרטים.
*
* שורות ריקות מפרידות בין פסקאות.
*
* הסבר מה עושה המחלקה או השיטה, בעזרת איזה אלגוריתם.
* הסבר את יחידות הארגומנטים וערכי החזרה.
*
* \note שים לב לכל מגבלות או הגבלות.
*
* (עבור פונקציות עם ארגומנטים או החזר בשווי:)
* \param foo ביטוי עצם קצר המתאר טיעון זה.
* \param bar הערה מקרה משפט, ותקופת סיום.
* \return ביטוי עצם קצר המתאר את הערך.
*
*\פנימי
*
* ניתן גם לדון בפרטי יישום פנימיים.
* הבנת החומר הזה לא צריכה להיות נחוצה לשימוש
* המחלקה או השיטה.
*/
דוגמה לכיתה
בסגנון זה בלוק ההערות של Doxygen מתחיל בשני תווים '*': / **, וקודם
הפריט המתועד.
עבור פריטים הזקוקים לתיאור קצר בלבד, מתאימה אחת מהטפסים הקצרים הבאים:
/** יישום Destructor. */
void DoDispose ();
int m_count; //!< ספירה של ...
שימו לב לצורה המיוחדת של הערת סוף השורה, //!, המציין שהוא מתייחס ל-
קודם פריט.
כמה פריטים שיש לשים לב אליהם:
· השתמש באותיות גדולות של משפט, כולל אות ראשונית.
· השתמש בסימני פיסוק, במיוחד ב'.'ים בסוף משפטים או ביטויים.
· \קָצָר אין צורך בתג; המשפט הראשון ישמש כתקציר
תאור.
כל מחלקה, שיטה, typedef, משתנה איבר, ארגומנט פונקציה וערך החזרה צריכים
להיות מתועד בכל קובצי קוד המקור המהווים את ה-API הפורמלי והיישום עבור
ns-3, כמו src/ /דֶגֶם/*, src/ /עוֹזֵר/* ו src/ /utils/*.
תיעוד עבור פריטים ב src/ /מִבְחָן/* ו src/ /examples/* מועדף,
אך לא חובה.
מוֹעִיל תכונות
· חברים שעברו בירושה יירשו אוטומטית מסמכים מההורה, (אך ניתן להחלפה
לפי תיעוד מקומי).
1. לתעד את מחלקת הבסיס.
2. בתת המחלקה סמן פונקציות שעברו בירושה עם הערה רגילה:
// שיטות בירושה
ריק וירטואלי FooBar (ריק);
Virtual int BarFoo (באז כפול);
שימו לב שהחתימות צריכות להתאים בדיוק, אז כלול את הארגומנט הפורמלי (בָּטֵל)
זה לא עובד עבור פונקציות סטטיות; לִרְאוֹת GetTypeId, להלן, לדוגמא.
בִּניָן חמצן Docs
בניית תיעוד Doxygen היא די פשוטה:
$ ./waf דוקסגן
זה בונה באמצעות תצורת ברירת המחדל, אשר מייצרת קטעי תיעוד עבור
את כל פריטים, גם אם אין להם בלוקים של תיעוד הערות מפורש. יש לזה את
השפעה של דיכוי אזהרות עבור פריטים לא מתועדים, אבל מוודא שהכל מופיע
בפלט שנוצר.
בעת כתיבת תיעוד, לעתים קרובות יותר שימושי לראות אילו פריטים יוצרים
אזהרות, בדרך כלל לגבי תיעוד חסר. כדי לראות את רשימת האזהרות המלאה, השתמש ב-
doc/doxygen.warnings.report.sh תַסרִיט:
$ doc/doxygen.warnings.report.sh
Waf: נכנס לספרייה `build'
...
Waf: עוזב את הספרייה 'build'
'בנייה' הסתיים בהצלחה (3m24.094s)
בנייה מחדש של מסמכי Doxygen עם מלא שגיאות...בוצע.
דיווח על אזהרות דוקסיגן
----------------------------------------
(כל הספירות הן גבולות תחתונים.)
אזהרות לפי מודול/ספרייה:
ספריית ספירה
----- ----------------------------------
3844 src/lte/model
1718 src/wimax/דגם
1423 src/core/model
....
138 פרמטרים לא מתועדים נוספים.
----------------------------------------
15765 אזהרות בסך הכל
126 מדריכים עם אזהרות
אזהרות לפי קובץ (אלפביתי)
קובץ ספירה
----- ----------------------------------
17 doc/introspected-doxygen.h
15 דוגמאות/ניתוב/manet-routing-compare.cc
26 דוגמאות/סטטיסטיקות/wifi-example-apps.h
....
----------------------------------------
967 קבצים עם אזהרות
אזהרות לפי קובץ (מספרי)
קובץ ספירה
----- ----------------------------------
374 src/lte/model/lte-asn1-header.h
280 src/lte/model/lte-rrc-sap.h
262 src/lte/model/lte-rrc-header.h
....
----------------------------------------
967 קבצים עם אזהרות
סיכום אזהרות דוקסגן
----------------------------------------
126 מדריכים
קבצי 967
15765 אזהרות
הסקריפט משנה את התצורה כדי להציג את כל האזהרות וכדי לקצר את זמן הריצה.
כפי שאתה יכול לראות, בכתיבה זו יש לנו a מגרש של פריטים לא מתועדים. הדו"ח
מסכם אזהרות לפי מודול src/*/*, ולפי קובץ, בסדר אלפביתי ומספרי.
לסקריפט יש כמה אפשרויות לסדר דברים ולהפוך את זה לניירות יותר. לעזרה,
להשתמש -h אוֹפְּצִיָה. לאחר שהרצתי אותו פעם אחת כדי לבצע את ה-Doxygen לבנות וליצור את המלאה
יומן אזהרות, אתה יכול לעבד מחדש את קובץ היומן עם "מסננים" שונים ללא צורך לעשות
ה-Doxygen המלא לבנות על ידי, שוב באמצעות -s אוֹפְּצִיָה. אתה יכול לא לכלול אזהרות מ
*/examples/* קבצים (-e אפשרות), ו/או */מִבְחָן/* קבצים (-t).
אולי האפשרות השימושית ביותר בעת כתיבת הערות תיעוד היא -m , אשר
יגביל את הדוח רק לקבצים תואמים src/ /*, ועקוב אחר הדוח עם
קווי האזהרה בפועל. לשלב עם -ואז ואתה יכול להתמקד באזהרות שכן
הכי דחוף במודול בודד:
$ doc/doxygen.warnings.report.sh -m mesh/helper
...
סיכום אזהרות דוקסגן
----------------------------------------
1 מדריכים
קבצי 3
149 אזהרות
אזהרות מסוננות
========================================
src/mesh/helper/dot11s/dot11s-installer.h:72: אזהרה: חבר m_root (משתנה) במחלקה ns3::Dot11sStack אינו מתועד.
src/mesh/helper/dot11s/dot11s-installer.h:35: אזהרה: סוג החזרה של חבר ns3::Dot11sStack::GetTypeId אינו מתועד
src/mesh/helper/dot11s/dot11s-installer.h:56: אזהרה: סוג החזרה של חבר ns3::Dot11sStack::InstallStack אינו מתועד
src/mesh/helper/flame/lfame-installer.h:40: אזהרה: חבר GetTypeId() (פונקציה) של מחלקה ns3::FlameStack אינו מתועד.
src/mesh/helper/flame/flame-installer.h:60: אזהרה: סוג החזרה של חבר ns3::FlameStack::InstallStack אינו מתועד
src/mesh/helper/mesh-helper.h:213: אזהרה: חבר m_nממשקים (משתנה) של מחלקה ns3::MeshHelper אינם מתועדים.
src/mesh/helper/mesh-helper.h:214: אזהרה: חבר m_spreadChannelPolicy (משתנה) של מחלקה ns3::MeshHelper אינו מתועד.
src/mesh/helper/mesh-helper.h:215: אזהרה: חבר m_stack (משתנה) של מחלקה ns3::MeshHelper אינו מתועד.
src/mesh/helper/mesh-helper.h:216: אזהרה: חבר m_stackFactory (משתנה) במחלקה ns3::MeshHelper אינו מתועד.
src/mesh/helper/mesh-helper.h:209: אזהרה: הפרמטרים של חבר ns3::MeshHelper::CreateInterface אינם (כולם) מתועדים
src/mesh/helper/mesh-helper.h:119: אזהרה: הפרמטרים של חבר ns3::MeshHelper::SetStandard אינם (כולם) מתועדים
עכשיו זה רק עניין של להבין את הקוד, ולכתוב כמה מסמכים!
ns-3 פרטים
לגבי הספינקס, הדוקסיגן Docs ו הפניה הם די טובים. לא נשכפל את
יסודות כאן, במקום להתמקד בשימוש מועדף עבור ns-3.
· השתמשו ב-Doxygen מודולים לקבץ פריטים קשורים.
בכותרת הראשית של מודול, צור קבוצת Doxgyen:
/ **
* \defgroup foo פרוטוקול Foo.
*/
סמן כל כיתה משויכת כשייכת לקבוצה:
/ **
* \ingroup foo
*
* סוג חבילת Foo.
*/
כיתה Foo
· האם ידעת טיפוסים יכול להיות טיעונים פורמליים? זה מאפשר תיעוד של הפונקציה
חתימות מצביעים:
/ **
* חתימת פונקציית התקשרות חוזרת בסרגל.
*
* \param ale גודל של חצי ליטר אייל, באונקיות אימפריאליות.
*/
typedef void (* BarCallback)(const int ale);
· העתק את תכונה מחרוזות עזרה מה GetTypeId שיטה להשתמש בתור בריף
תיאורים של חברים קשורים.
· \bugid{298} ייצור קישור לבאג 298 ב-Bugzilla שלנו.
· \pname{foo} בתיאור יפורמט Foo בתור \param Foo פרמטר, מה שמבהיר
שאתה מתכוון לטיעון ממשי.
· \RFC{301} ייצור קישור ל-RFC 301.
· \פְּנִימִי יש להשתמש רק כדי להתחיל דיון על פרטי יישום, לא כדי
סימן פְּרָטִי פונקציות (הן כבר מסומנות, כמו פְּרָטִי!)
· אל תיצור כיתות עם שמות טריוויאליים, כגון בכיתה A, אפילו בחבילות בדיקה. אלה
לגרום לכל המופעים של שם המחלקה המילולי 'A' להיות מוצגים כקישורים.
כפי שצוין לעיל, פונקציות סטטיות אינן יורשות את התיעוד של אותן פונקציות ב
כיתת ההורים. ns-3 משתמש בכמה פונקציות סטטיות בכל מקום; המוצע
בלוק התיעוד למקרים אלה הוא:
· בנאי/הרס ברירת מחדל:
הכיתה שלי (); //!< בנאי ברירת מחדל
~MyClass (); //!< Destructor
· משחת דמה ו-DoDispose:
/** משחת דמה, ראה DoDispose. */
~MyClass ();
/** יישום Destructor */
ריק וירטואלי DoDispose ();
· GetTypeId:
/ **
* רשום סוג זה.
* \return האובייקט TypeId.
*/
סטטי TypeId GetTypeId (ריק);
מפעיל קבוצות משנה of ns-3 מודולים
כמו ברוב פרויקטי התוכנה, ns-3 הולך וגדל במונחים של מספר מודולים,
שורות קוד וטביעת זיכרון. עם זאת, משתמשים עשויים להשתמש רק בכמה מהמודולים הללו
בכל פעם. מסיבה זו, ייתכן שמשתמשים ירצו להפעיל במפורש רק את תת-קבוצת ה-
אפשרי ns-3 מודולים שהם באמת צריכים למחקר שלהם.
פרק זה דן כיצד להפעיל רק את ns-3 מודולים שאתה מתעניין בהם
באמצעות.
איך ל לאפשר a תת-קבוצה of ns-3's מודולים
אם נבנות ספריות משותפות, הפעלת מודול תגרום לפחות אחת
ספרייה שתיבנה:
libns3-modulename.so
אם למודול יש ספריית בדיקה וספריות בדיקה נבנות, אז
libns3-modulename-test.so
ייבנה גם כן. מודולים אחרים שהמודול תלוי בהם וספריות הבדיקה שלהם
גם ייבנה.
כברירת מחדל, כל המודולים מובנים ns-3. ישנן שתי דרכים לאפשר תת-קבוצה של אלה
מודולים:
1. שימוש באפשרות --enable-modules של waf
2. באמצעות ns-3 קובץ תצורה
אפשר מודולים באמצעות של וואף --enable-modules אוֹפְּצִיָה
כדי להפעיל רק את מודול הליבה עם דוגמה ובדיקות, למשל, נסה את הפקודות הבאות:
$ ./waf נקי
$ ./waf configure --enable-examples --enable-tests --enable-modules=core
$ ./waf build
$ cd build/debug/
ש"ס
והספריות הבאות צריכות להיות נוכחות:
כריכות libns3-core.so ns3 scratch utils
דוגמאות libns3-core-test.so דוגמאות src
הערה ./ואף לְנַקוֹת השלב נעשה כאן רק כדי שיהיה ברור יותר אילו ספריות מודול
נבנו. אתה לא צריך לעשות ./ואף לְנַקוֹת על מנת לאפשר קבוצות משנה של מודולים.
הפעלת test.py תגרום להפעיל רק את הבדיקות התלויות בליבת המודול:
24 מתוך 24 מבחנים עבר (24 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
חזור על השלבים לעיל עבור מודול "רשת" במקום מודול "הליבה", ואת
הבאות ייבנו, מכיוון שהרשת תלויה בליבה:
כריכות libns3-core.so libns3-network.so ns3 scratch utils
דוגמאות libns3-core-test.so libns3-network-test.so דוגמאות src
הפעלת test.py תגרום לבדיקות אלה התלויות רק במודולי הליבה והרשת
להפעיל:
31 מתוך 31 מבחנים עבר (31 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
אפשר מודולים באמצעות מה היא ns-3 תצורה פילה
קובץ תצורה, .ns3rc, נוסף ל ns-3 המאפשר למשתמשים לציין איזה
יש לכלול מודולים ב-build.
בעת הפעלת תת-קבוצה של ns-3 מודולים, כללי העדיפות הם כדלקמן:
1. המחרוזת --enable-modules configure עוקפת כל קובץ ns3rc
2. קובץ .ns3rc ברמה העליונה ns-3 המדריך הבא ייבדק, אם קיים
3. המערכת מחפשת ~/.ns3rc אם השניים לעיל אינם מפורטים
אם אף אחד מהאמור לעיל לא מגביל את המודולים שייבנו, כל המודולים ש-waf יודע עליהם יעשו זאת
להיבנות.
הגרסה המתוחזקת של קובץ .ns3rc ב- ns-3 מאגר קוד המקור נמצא בתוכו
מה היא כלים מַדרִיך. הסיבה לכך היא אם זה היה בספרייה ברמה העליונה של
מאגר, זה יהיה מועד לביצוע צ'קים מקריים מתחזקים המאפשרים את
מודולים שהם רוצים להשתמש בהם. לכן, משתמשים צריכים להעתיק ידנית את ה-.ns3rc מה-
כלים ספרייה למקום המועדף עליהם (ספרייה ברמה העליונה או ספריית הבית שלהם).
אפשר תצורת בנייה מודולרית מתמשכת.
בהנחה שאתה בדרג העליון ns-3 ספריה, אתה יכול לקבל עותק של .ns3rc
קובץ שנמצא ב- כלים ספרייה כדלקמן:
$ cp utils/.ns3rc .
קובץ ns3rc אמור להיות כעת ברמה העליונה שלך ns-3 ספרייה, והיא מכילה את
הבא:
#! /usr/bin/env פִּיתוֹן
# רשימה של המודולים שיופעלו כאשר ns-3 מופעל.
# מודולים התלויים במודולים הרשומים יופעלו גם כן.
#
# ניתן להפעיל את כל המודולים על ידי בחירה ב'all_modules'.
modules_enabled = ['all_modules']
# הגדר את זה שווה ל-true אם אתה רוצה להפעיל דוגמאות.
examples_enabled = False
# הגדר את זה כשווה ל-true אם ברצונך להפעיל בדיקות.
tests_enabled = False
השתמש בעורך המועדף עליך כדי לשנות את קובץ ה-.ns3rc כך שיאפשר רק את מודול הליבה עם
דוגמאות ומבחנים כמו זה:
#! /usr/bin/env פִּיתוֹן
# רשימה של המודולים שיופעלו כאשר ns-3 מופעל.
# מודולים התלויים במודולים הרשומים יופעלו גם כן.
#
# ניתן להפעיל את כל המודולים על ידי בחירה ב'all_modules'.
modules_enabled = ['core']
# הגדר את זה שווה ל-true אם אתה רוצה להפעיל דוגמאות.
examples_enabled = נכון
# הגדר את זה כשווה ל-true אם ברצונך להפעיל בדיקות.
tests_enabled = נכון
רק מודול הליבה יופעל כעת אם תנסה את הפקודות הבאות:
$ ./waf נקי
$ ./waf להגדיר
$ ./waf build
$ cd build/debug/
ש"ס
והספריות הבאות צריכות להיות נוכחות:
כריכות libns3-core.so ns3 scratch utils
דוגמאות libns3-core-test.so דוגמאות src
הערה ./ואף לְנַקוֹת השלב נעשה כאן רק כדי שיהיה ברור יותר אילו ספריות מודול
נבנו. אתה לא צריך לעשות ./ואף לְנַקוֹת על מנת לאפשר קבוצות משנה של מודולים.
הפעלת test.py תגרום להפעיל רק את הבדיקות התלויות בליבת המודול:
24 מתוך 24 מבחנים עבר (24 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
חזור על השלבים לעיל עבור מודול "רשת" במקום מודול "הליבה", ואת
הבאות ייבנו, מכיוון שהרשת תלויה בליבה:
כריכות libns3-core.so libns3-network.so ns3 scratch utils
דוגמאות libns3-core-test.so libns3-network-test.so דוגמאות src
הפעלת test.py תגרום לבדיקות אלה התלויות רק במודולי הליבה והרשת
להפעיל:
31 מתוך 31 מבחנים עבר (31 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
הפעלה/השבתה ns-3 בדיקות ו דוגמאות
השמיים ns-3 ההפצה כוללת דוגמאות ומבחנים רבים המשמשים לאימות ns-3
מערכת. עם זאת, ייתכן שמשתמשים לא תמיד ירצו שהדוגמאות והבדיקות הללו יופעלו עבורם
התקנה של ns-3.
פרק זה דן כיצד לבנות ns-3 עם או בלי הדוגמאות והמבחנים שלה.
איך ל הפעל / השבת דוגמאות ו בדיקות in ns-3
ישנן 3 דרכים להפעיל/לבטל דוגמאות ובדיקות ב ns-3:
1. שימוש ב-build.py when ns-3 נבנה לראשונה
2. משתמשים בוואף פעם אחת ns-3 נבנה
3. באמצעות ns-3 קובץ תצורה פעם אחת ns-3 נבנה
הפעל / השבת דוגמאות ו בדיקות באמצעות build.py
אתה יכול להשתמש ב-build.py כדי להפעיל/להשבית דוגמאות ובדיקות כאשר ns-3 בנוי לראשון
הזמן.
כברירת מחדל, דוגמאות ומבחנים אינם מובנים ns-3.
מהספרייה ns-3-allinone, אתה יכול לבנות ns-3 בלי דוגמאות או בדיקות פשוט
על ידי:
$ ./build.py
הפעלת test.py ברמה העליונה ns-3 ספרייה כעת לא תגרום לכך שלא יהיו דוגמאות או בדיקות
לרוץ:
0 מתוך 0 מבחנים עבר (0 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
אם תרצה לבנות ns-3 עם דוגמאות ומבחנים, ולאחר מכן בצע את הפעולות הבאות מתוך
ספריית ns-3-allinone:
$ ./build.py --enable-examples --enable-tests
הפעלת test.py ברמה העליונה ns-3 ספרייה תגרום לכל הדוגמאות והבדיקות
להפעיל:
170 מתוך 170 מבחנים עבר (170 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
הפעל / השבת דוגמאות ו בדיקות באמצעות WAF
אתה יכול להשתמש ב-waf כדי להפעיל/להשבית דוגמאות ובדיקות פעם אחת ns-3 נבנה.
כברירת מחדל, דוגמאות ומבחנים אינם מובנים ns-3.
מהרמה העליונה ns-3 ספרייה, אתה יכול לבנות ns-3 בלי דוגמאות או בדיקות פשוט
על ידי:
$ ./waf להגדיר
$ ./waf build
הפעלת test.py כעת לא תגרום לכך שלא יופעלו דוגמאות או בדיקות:
0 מתוך 0 מבחנים עבר (0 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
אם תרצה לבנות ns-3 עם דוגמאות ומבחנים, ואז בצע את הפעולות הבאות מלמעלה
רָמָה ns-3 Directory:
$ ./waf configure --enable-examples --enable-tests
$ ./waf build
הפעלת test.py תגרום להרצת כל הדוגמאות והבדיקות:
170 מתוך 170 מבחנים עבר (170 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
הפעל / השבת דוגמאות ו בדיקות באמצעות מה היא ns-3 תצורה פילה
קובץ תצורה, .ns3rc, נוסף ל ns-3 המאפשר למשתמשים לציין אם
יש לבנות דוגמאות ומבחנים או לא. אתה יכול להשתמש בקובץ זה כדי להפעיל/להשבית
דוגמאות ומבחנים פעם אחת ns-3 נבנה.
בעת הפעלת השבתת דוגמאות ובדיקות, כללי העדיפות הם כדלקמן:
1. המחרוזות --enable-examples/--disable-examples מחליפות כל קובץ ns3rc
2. המחרוזות של --enable-tests/--disable-tests עוקפות כל קובץ ns3rc
3. קובץ .ns3rc ברמה העליונה ns-3 המדריך הבא ייבדק, אם קיים
4. המערכת מחפשת ~/.ns3rc אם קובץ .ns3rc לא נמצא בשלב הקודם
אם אף אחד מהאמור לעיל לא קיים, אז דוגמאות ומבחנים לא ייבנו.
הגרסה המתוחזקת של קובץ .ns3rc ב- ns-3 מאגר קוד המקור נמצא בתוכו
מה היא כלים מַדרִיך. הסיבה לכך היא אם זה היה בספרייה ברמה העליונה של
מאגר, זה יהיה מועד לביצוע צ'קים מקריים מתחזקים המאפשרים את
מודולים שהם רוצים להשתמש בהם. לכן, משתמשים צריכים להעתיק ידנית את ה-.ns3rc מה-
כלים ספרייה למקום המועדף עליהם (ספרייה ברמה העליונה או ספריית הבית שלהם).
לאפשר הפעלה מתמשכת של דוגמאות ובדיקות.
בהנחה שאתה בדרג העליון ns-3 ספריה, אתה יכול לקבל עותק של .ns3rc
קובץ שנמצא ב- כלים ספרייה כדלקמן:
$ cp utils/.ns3rc .
קובץ ns3rc אמור להיות כעת ברמה העליונה שלך ns-3 ספרייה, והיא מכילה את
הבא:
#! /usr/bin/env פִּיתוֹן
# רשימה של המודולים שיופעלו כאשר ns-3 מופעל.
# מודולים התלויים במודולים הרשומים יופעלו גם כן.
#
# ניתן להפעיל את כל המודולים על ידי בחירה ב'all_modules'.
modules_enabled = ['all_modules']
# הגדר את זה שווה ל-true אם אתה רוצה להפעיל דוגמאות.
examples_enabled = False
# הגדר את זה כשווה ל-true אם ברצונך להפעיל בדיקות.
tests_enabled = False
מהרמה העליונה ns-3 ספרייה, אתה יכול לבנות ns-3 בלי דוגמאות או בדיקות פשוט
על ידי:
$ ./waf להגדיר
$ ./waf build
הפעלת test.py כעת לא תגרום לכך שלא יופעלו דוגמאות או בדיקות:
0 מתוך 0 מבחנים עבר (0 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
אם תרצה לבנות ns-3 עם דוגמאות ומבחנים, השתמש בעורך המועדף עליך כדי לשנות
הערכים בקובץ .ns3rc עבור קובץ examples_enabled ו-tests_enabled להיות True:
#! /usr/bin/env פִּיתוֹן
# רשימה של המודולים שיופעלו כאשר ns-3 מופעל.
# מודולים התלויים במודולים הרשומים יופעלו גם כן.
#
# ניתן להפעיל את כל המודולים על ידי בחירה ב'all_modules'.
modules_enabled = ['all_modules']
# הגדר את זה שווה ל-true אם אתה רוצה להפעיל דוגמאות.
examples_enabled = נכון
# הגדר את זה כשווה ל-true אם ברצונך להפעיל בדיקות.
tests_enabled = נכון
מהרמה העליונה ns-3 ספרייה, אתה יכול לבנות ns-3 עם דוגמאות ומבחנים פשוט על ידי
עושה:
$ ./waf להגדיר
$ ./waf build
הפעלת test.py תגרום להרצת כל הדוגמאות והבדיקות:
170 מתוך 170 מבחנים עבר (170 עבר, 0 דילג, 0 נכשל, 0 קרס, 0 שגיאות valgrind)
פתרון בעיות
פרק זה מציג מידע על שגיאות נפוצות בבנייה או בהפעלה
ns-3 תוכניות.
שימו לב שהוויקי (http://www.nsnam.org/wiki/Troubleshooting) אולי תרם
פריטים.
לִבנוֹת שגיאות
זמן ריצה שגיאות
לפעמים, שגיאות יכולות להתרחש בתוכנית לאחר בנייה מוצלחת. אלה הם זמן ריצה
שגיאות, ויכולות להתרחש בדרך כלל כאשר הזיכרון פגום או ערכי המצביע הם באופן בלתי צפוי
ריק.
הנה דוגמה למה שעלול להתרחש:
$ ./waf --הפעל tcp-point-to-point
נכנס לספרייה '/home/tomh/ns-3-nsc/build'
האוסף הסתיים בהצלחה
הפקודה ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] יצאה עם הקוד -11
הודעת השגיאה אומרת שהתוכנית הסתיימה ללא הצלחה, אך היא לא ברורה
מהמידע הזה מה עלול להיות לא בסדר. כדי לבחון מקרוב יותר, נסה להפעיל אותו תחת
מה היא gdb הבאגים:
$ ./waf --הפעל tcp-point-to-point --command-template="gdb %s"
נכנס לספרייה '/home/tomh/ns-3-nsc/build'
האוסף הסתיים בהצלחה
GNU gdb Red Hat Linux (6.3.0.0-1.134.fc5rh)
זכויות יוצרים 2004 Free Software Foundation, Inc.
GDB היא תוכנה חופשית, המכוסה על ידי הרישיון הציבורי הכללי של גנו, ואתה כן
מוזמן לשנות אותו ו/או להפיץ עותקים שלו בתנאים מסוימים.
הקלד "הצג העתקה" כדי לראות את התנאים.
אין שום אחריות עבור GDB. הקלד "הצג אחריות" לפרטים.
GDB זה הוגדר כ-"i386-redhat-linux-gnu"...באמצעות מארח libthread_db
ספריית "/lib/libthread_db.so.1".
(gdb) לרוץ
תוכנית התחלה: /home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point
קריאת סמלים מאובייקט משותף שנקרא מזיכרון היעד...בוצע.
מערכת טעון מסופקת DSO ב-0xf5c000
התוכנית קיבלה אות SIGSEGV, תקלה בפילוח.
0x0804aa12 ב-main (argc=1, argv=0xbfdfefa4)
בכתובת ../examples/tcp-point-to-point.cc:136
136 Ptr localSocket = socketFactory->CreateSocket ();
(gdb) p localSocket
$1 = {m_ptr = 0x3c5d65}
(gdb) p socketFactory
$2 = {m_ptr = 0x0}
(gdb) הפסיק
התוכנית פועלת. לצאת בכל זאת? (י או n) y
שים לב תחילה לדרך שבה התוכנית הופעלה - העבר את הפקודה להפעלה כארגומנט ל-
תבנית פקודה "gdb %s".
זה אומר לנו שהיה ניסיון להפנות את ההתייחסות לשקע מצביע nullFactory.
בואו נסתכל סביב שורה 136 של tcp-point-to-point, כפי ש-gdb מציע:
Ptr socketFactory = n2->GetObject (Tcp::iid);
Ptr localSocket = socketFactory->CreateSocket ();
localSocket->Bind ();
האשם כאן הוא שערך ההחזר של GetObject לא נבדק ואולי כן
ריק.
לפעמים ייתכן שתצטרך להשתמש ב- ולגרינד זיכרון בודק לשגיאות עדינות יותר. שוב,
אתה קורא לשימוש בוולגרינד באופן דומה:
$ ./waf --הפעל tcp-point-to-point --command-template="valgrind %s"
מקור
מסמך זה כתוב ב ReStructuredText ל ספינקס ומתוחזק ב
מסמך/מדריך ספריית קוד המקור של ns-3.
השתמש ב-ns-3-manual באינטרנט באמצעות שירותי onworks.net
