חבילת תרחישים ל- MesSim מדריךעזרלמפתח דינאמיים עופר ספיבק, גיא חפץ 01/01/2011
MesSim דינאמיים - חבילת תרחישים ל- P_Scenario 1. תוכן עניינים א. מבנה החבילה...3 מנהל התרחיש 3... CsenarioManager 2. 3. 4. 5. פרסר CscriptParser 3... פרסר ספציפי CspecificCommandParser 3... פקודה 4...Ccommand פקודת תנאי 4... CconditionalCommand 6. 7. ב. הסבר ג. הסבר למבנה פרמטרי פקודה 4...CcommandParams מנהל הפקודות 4...CcommandManager לצריכת החבילה...8 קובץ הסקריפט...10 ד. הוספת פקודות חדשות...15 הסברכלליעל פעולת הParser...15 הרחבת מכלול הפקודות...19 ה. רתמתבדיקות...21 ו. בדיקותיחידה...24 2
א. מבנה החבילה מחלקותעיקריות:.1 מנהל התרחיש CsenarioManager זוהי המחלקה העיקרית בחבילה מחלקה זו מייצגת את התרחיש שרץ ופועלתכ- THREAD עצמאי..2 פרסר CscriptParser מתרגם את קובץ הקלט (מפורמטxml ) ובונהאת מבנה הנתונים שמייצג את התרחיש..3 פרסר ספציפי CspecificCommandParser מחלקה אבסטרקטית לשימושעתידי המאפשרת הוספת פקודות חדשות לאלו הקיימותבמחלקהללאפתיחת הקוד הקיים. התפקיד של מחלקה שיורשת ממחלקה זו, יהיהלתרגם את השורות הרלוונטיות בקובץ הקלט, ולבנות את מבנה הנתונים המייצג את הפקודה החדשה (ראהפרקד'). 3
מחלקותפנימיות:.4 פקודה Ccommand מחלקה אבסטרקטית המייצגת פקודהיחידה. לפקודהפונקציהוירטואליתrun אשר צריכהלממשאתריצת הפקודה. עלפונקציהזולהחזירtrue אלאאםכןעקב תוצאת הריצה שלה ישלסיים את ריצת התרחיש. את מחלקה זויורשותכלל מחלקות הפקודות ) Halt, Send, Assign,.(Conditional_Command.5 פקודת תנאי CconditionalCommand מחלקה אבסטרקטית היורשת מ-.CCommand את מחלקה זויורשותכל המחלקות שמייצגותפקודות הבודקות תנאי.(If,Loop,Wait).6 פרמטרי פקודה CcommandParams מחלקה אבסטרקטית המייצגת את כלל הפרמטרים הנחוצים לפקודה. עלמנת לאחסן את הפרמטרים של פקודה ספציפית כלשהי ישלרשת ממחלקה זו..7 מנהל הפקודות CcommandManager מחלקהזו מכילה אתכל הפקודות ומריצהאותן אחת אחרי השנייה עד שאחת הפקודות מחזירה את הערך false דבר המהווה אינדיקציה שהתרחיש "מבקש" לסיים את עצמו. 8. הפקודות הנתמכות א. ב. פקודת שליחת הודעה CSendMessageCommand פונקצית Run ניגשת ישירותל- OutgoingMessageManager ומפעילה את הפונקציה OutgoingMessageManager כתוצאה מכך (כידוע, GenerateMessageByID מייצראת ההודעה, מכניס אותה לתור ההודעות ומסמןל- Thread שלה- SenderTask להתעוררולשלוח את ההודעה). פקודת המתנה לאירועCWaitForEventCommand פונקצית Run רושמת מאזיןל- MesSimTimer (ומריצה את ה- MesSimTimer לפרק הזמן הרצוי), ומאזיןלכלל ההודעות הנכנסות. לאחר שהמאזינים נרשמו, הפונקציה ממתינהעלeventFlag. המאזיןלשעוןיתעוררכשיעבורפרק הזמן הרצוי (שאליו אותחל השעון), ויסמןלפקודהלהמשיך. המאזיןלהודעות יתעוררכלפעם שמתקבלת הודעה, ויבדוק האם ההודעה תואמתלאחד התנאים הרצויים. במידהוההודעה מתאימה, יסמןעלה- eventflag לפקודהלהמשיך, ואם ההודעה אינה מתאימה, ימשיךלהאזין. 4
בפועל האירוע הראשון שמתרחש (קבלת אחת מההודעות הרצויות / מעבר פרק הזמן שהוגדר), יגרוםלהמשך הריצה. ג. פקודת תנאי - CIfCommand פונקצית Run רושמת מאזין לכלל ההודעות הנכנסות (ע"יהפעלתפונקציהשלה- superclass המחלקה.(CConditionalCommand לאחר שהמאזינים נרשמו, הפונקציה ממתינה על.eventFlag המאזיןלהודעות יתעוררעםקבלת הודעה, ויבדוק האם ההודעה תואמת לאחד התנאים הרצויים. במידהוההודעה מתאימה, ייתןפקודתRun למשתנה המקומי- m_pcifcommandmanager (משתנה מסוג CCommandManager המכיל אתכל הפקודות הנדרשות לביצועבמידהוהתנאי מתקיים). אם ההודעה אינה מתאימה לאחד התנאים תינתן פקודתRun למשתנה המקומי.m_pcElseCommandManager Seq Diagram לפקודתתנאי 5
ד. ה. ו. פקודתלולאהCLoopCommand - פונקצית Run רושמת מאזין לכלל ההודעות הנכנסות. לאחר שהמאזינים נרשמו, הפונקציה ממתינה על.eventFlag המאזיןלהודעות יתעוררכלפעם שמתקבלת הודעה, ויבדוק האם ההודעה תואמתלאחד התנאים הרצויים. במידהוההודעה מתאימה, ייתןפקודתRun למשתנה המקומי- m_pcloopcommandmanager (משתנה מסוג CCommandManager המכיל את כל הפקודות הנדרשות לביצוע של פעם בה התנאי מתקיים) וימשיךלהאזין. אם ההודעה אינה מתאימה, ריצת הפקודה תופסק, ויבוצע מעבר לפקודה הבאה במנהל הפקודות. פקודת השמת ערךלהודעהCAssignCommand פונקציית Run רושמת Modifier ב- OutgoingMessageManager עבורOpcode ההודעה הרצוי, ומסתיימת (מעברלפקודה הבאה במנהל הפקודות). כאשרבהמשך התרחיש נשלחת הודעה בעלתאותו ה- Opcode ה, - OutgoingMessageManager מעיר את כלה- Modifiers לפני שליחת הודעה, ובפרט מעיר את המאזין שה- AssignCommand רשמהכדילעדכן את ההודעה לפני שליחתה. ה- Modifier שהתעורר מעדכן את כל השדות, ע"פ הסדר. ישלצייןכיכלל השדות בהודעה שלא הוכנסו כפרמטרים לCAssignCommand יישארוללא שינוי. בנוסף ה- Modifier האחרון שנרשם הוא שקובע (כלומר, בהכנסת 2 פקודות Assign עוקבות העובדות על אותם השדות עדכון השדות יהיה מצטבר: ראשית הפקודה הראשונה תעדכן, ועל השדות המעודכנים, תעדכן הפקודה השנייה). פקודתעצירהCHaltCommand פונקציית Run מחזירה False דבר המתורגםלעצירת ה- CCommandManger בהיררכיה הגבוהה ביותר (בדומהלפקודתbreak בשפהעלית). 6
מבנהלדוגמא: 7
ב. הסבר לצריכת החבילה 1 3 2 4 ישלכתובאת מהלך התסריט הרצוי ולשמור אותו בקובץxml (ראהפרקג'). ישליצוראובייקט של המחלקה :CScriptParser לבנאיישלהעביר מחרוזת המייצגת את הנתיב (path) בדיסקלקובץ הסקריפט (קובץ ה-.(xml (0 (1 הערה: במידהוהוספופקודות חדשות לחבילה שסופקה, ישלהוסיף את הפרסרים הספציפיים שלהם לאובייקט הנוצר בשלבזה (ראהפרקד'). o יצירתאובייקט של CSenarioManager תבוצעע"יקריאהלפונקציה pccommandparser) CreateSenarioManager(CCommandParser* של המחלקה CSystemFactory (הפרמטר המועבר הוא מצביע לאובייקט שנוצר בשלב הראשון). הפונקציה מחזירה מצביע לאובייקט שנוצר. ישלהפעיל את הפונקציה Start של אובייקט ה-.CSenarioManager פונקציהזויוצרת THREAD חדש שאחראי על הרצת התרחיש הדינאמי : o פונקצית התרגום של ה- parser נקראתעלמנתלבנות את מבנה הנתונים מקובץ הקלט. (2 (3 8
ה( o לאחר סיום התרגום, ה- thread מתחיל לרוץעל התסריט עדלסיומו. ממתין עלeventFlag. Thread עםסיוםהתרחיש, ה- o עלמנתלהריץ שוב את התרחיש ניתןלקרואלפונקציתReStart של אובייקט ה-,CSenarioManager אשר תסמן ל- Thread בעזרת ה- eventflag לבצעריצהנוספת על התרחיש (ללאפענוח הפקודות מחדש). מחיקת מסודרת של אובייקט ה- CSenarioManager תבוצע ע"יקריאהלפונקציה. CSystemFactory של המחלקה CloseSenarioManager אובייקטCScriptParser שנוצר בשלב הראשון לאנמחקבפעולהזו, ואםלא משתמשיםבויותר, ישלמחוק אותו בנפרד (מחיקתו תמחק גםאתכל הפרסרים הספציפיים שהוספו לו). (4 (5 (6 הערות ישלוודא שכלל הערוצים (כלל האובייקטים של (CChannel נוצרוגםהםבאמצעות הinstance של המחלקה,CSystemFactory ולוודא שמספרי הערוצים שנוצרו תואמיםלמספרים הרצויים המופיעים בקובץ ה- THREAD - XML שלהתרחיש פונהלמחלקהזועלמנתלקבל את הערוץ לשליחת/ קבלת הודעות). במידהוהמשתמשרוצהלעבודרקע"פ התרחיש הדינאמי (ללא שימוש ב-,(pattern ביצירתכלערוץעליולהגדיר שלא יווצר THREAD לOutgoingMessageManager. (1 (2 דוגמתקודלצריכת החבילה //Create the parser CScriptParser *pcscriptparser = new CScriptParser("O:\\ScriptFile.xml"); //call the CSystemFactory in order to create the ScenarioManager CScenarioManager *pcsenariomanager= CSystemFactory::Instance().CreateSenarioManager(m_pcScriptParser); /* CREATE AND OPEN ALL THE CHANNELS USING CSystemFactory */ // start the SenarioMessage thread pcsenariomanager->start(); 9
ג. הסבר למבנהקובץ הסקריפט <Senario> <Commands Type="Main"> <Command Opcode =???> </Command> <Command Opcode =???> </Command> </Commands> </Senario>,Senario וכלל הפקודות נמצאות בין הסוגרים 1. מבנה כללי: הסקריפט נמצא בין בשםCommands. בשם הסוגרים 2. פקודת שליחת הודעה (Opcode=1) לפקודה 2 ארגומנטים מס' הערוץ, ומס' ההודעה למשלוח: <Command Opcode = "1" Name="Send"> <Arguments> <Arg Key = "Channel" Value = "1" /> <Arg Key = "Msg" Value = "1" /> </Arguments> </Command> 3. פקודת השמת שדות להודעה (Opcode=2) לפקודה 2 ארגומנטים קבועים מס' הערוץ ומספר ההודעה, ומספר משתנה של ארגומנטים המגדירים את השינוי הרצוי בשדות: השמתערךלשדה 1) = (Operation 3 ארגומנטים עוקביםהמגדיריםאת מספר השדה, ארגומנט הפקודה = 1, וארגומנט של הערך המספרי שיש להכניסלשדה. הוספתערךלשדה 2) = (Operation - 3 ארגומנטים עוקביםהמגדיריםאת מספר השדה, ארגומנט הפקודה = 2, וארגומנט של הערך המספרי שיש להוסיףלערך הקיים בשדה. 10
חיסורערך משדה (3 = (Operation באופן דומהלפקודה הקודמת. חיבור 2 שדות (4 = (Operation 4 ארגומנטים עוקבים המגדירים את מספרהשדה, ארגומנטהפקודה= 4, ו- 2 ארגומנטיםהמגדיריםאתמספרי השדותלחיבור. חיסור 2 שדות (5 = (Operation באופן דומהלפקודה הקודמת. לדוגמא הפקודה הבאה תתורגם לפקודתהשמהלהודעהמספר 2 שדה 0 יקבל את הערך 16, לשדה 1 יתווסף 7, ושדה 2 יקבלאתהסכוםשלשדות 0 ו- 1. <Command Opcode = "2" Name="Assign"> <Arguments> <Arg Key = "Channel" Value = "1" /> <Arg Key = "Msg" Value = "2" /> <Arg Key = "Field" Value = "0" /> <Arg Key = "Operation" Value = "1"/> <Arg Key = "Value" Value = "16"/> <Arg Key = "Field" Value = "1" /> <Arg Key = "Operation" Value = "2"/> <Arg Key = "Value" Value = "7"/> <Arg Key = "Field" Value = "2" /> <Arg Key = "Operation" Value = "4"/> <Arg Key = "Operator1" Value = "0"/> <Arg Key = "Operator2" Value = "1"/> </Arguments> </Command> הערה: השמתהערכיםמבוצעתע"פהסדר- למשל 2 יקבלאתסכום שדות 0 ו- 1 לאחר שאלו כברעברו את השינוי. בדוגמאזושדה.4 פקודת תנאי 3) = (Opcode לפקודה ארגומנט בודדאשרמגדיראתמספרהערוץ. התנאילפיופועלת הפקודה מוגדר בין סוגרים בשםConditions. כל תנאי מורכב ממספר הודעה, ומידע לגבי השדות אשר ייבדק מול ההודעה שהתקבלה. המידעלגבי השדות כוללאת הנתונים הבאים: מספר השדה הנדרש לבדיקה ("Key") מספרי השדות מתחילים מ- 0 ותואמים ע"פ הסדר לשדות שהוגדרו בקובץ ה- xml של ההודעות שהוכנס לבנאי של. CGenericMessageHeaderFactory הערך הנדרש לבדיקה.("Value") 11
סוגהנתון ("DataType") נבדקתתאימותלמולה- DataType של השדה סוג השדה נקבעגםהואבקובץ ה- xml של ההודעות ע"פ המפתח הבא: כידוע עצמו. הלוגיקה בודקת אם ההודעה שהתקבלה מתאימה לאחת or) בין ההודעות) מבין ההודעות שהוכנסוכתנאים. התאמה להודעהמוגדרתכהתאמהלכללהשדותבהודעהעצמה and) בין השדות). הפקודות הנדרשות לביצועבמקרהונמצאה התאמה מוגדרותבין הסוגריםcommands עם מחרוזת מזהה Type="True" (באופן דומה להגדרת הסוגרים commands בסעיף 1 בפרק זה), ובאופן דומה מוגדרות הפקודות לביצוע במידה ולא מתגלית התאמה להודעה שהתקבלה (עם המזהה.(Type="False" <Command Opcode = "3" Name="If"> <Arguments> <Arg Key = "Channel" Value = "1" /> </Arguments> <Conditions> <Message Opcode="1"> <Fields> <Field Key="1" DataType="5" Value="15" /> <Field Key="2" DataType="5" Value="15" /> </Fields> </Message> <Message Opcode="2"> <Fields> <Field Key="1" DataType="5" Value="6" /> <Field Key="3" DataType="5" Value="100" /> </Fields> </Message> </Conditions> <Commands Type="True"> 12
</Commands> <Commands Type="False"> </Commands> </Command>.5 פקודתלולאה 4) = (Opcode פועלתבאופןדומהלפקודה הקודמת. מכילהרקסוגראחדמסוגcommand המכיל את הפקודות לביצוע. בשונה מפקודת ה-,If פקודה זו מכילה ארגומנט נוסף עם המזהה "NotCondition" המהווהדגלעבורהיפוךהתנאי. במידה וערך ארגומנט זה הוא 0 פקודת ה- Loop תבצע מעבר אחד על כלל הפקודות שתחת הסוגרים <Commands> עם כל קבלת הודעה העונה לאחד התנאים שב- <Conditions> (מבצעתאתהפקודותכלעודהתקבלההודעההמקיימתאתהתנאי). במידהוערךארגומנטזההוא 1 הפקודהתבצעאתכללהפקודותכלעודהתקבלההודעה שאינהעונהלאףאחדמהתנאיםשב- <Conditions> (כלעודלאהתקבלההודעההמקיימת אתאחדהתנאים). <Command Opcode = "4" Name="Loop"> <Arguments> <Arg Key = "Channel" Value = "1" /> <Arg Key = "NotCondition" Value = "0" /> </Arguments> <Conditions> <Message Opcode="1"> </Message> <Message Opcode="2"> <Fields> <Field Key="0" DataType="5" Value="42" /> <Field Key="1" DataType="5" Value="42" /> </Fields> </Message> </Conditions> <Commands Type="Do"> <Command Opcode = "1" Name="Send"> </Command> </Commands> </Command> 13
6. פקודת המתנה לאירוע (Opcode=5) מכילה 2 ארגומנטים מספר הערוץ, ופרק הזמן ב- ms שהיא נדרשתלחכותלהודעה. מכילה תנאים לבדיקהבדומהלתנאים שהוגדרו בפקודת התנאי. הפקודה הזוממתינהעדשמתקבלתהודעה המתאימהלפרמטריםשהוכנסו אועדשיחלוף פרק הזמן שהוגדר (הראשון מביניהם). <Command Opcode = "5" Name="Wait"> <Arguments> <Arg Key = "Channel" Value = "1"/> <Arg Key = "WaitPeriod" Value = "3000"/> </Arguments> <Conditions> <Message Opcode="1"> <Fields> </Fields> </Message> </Conditions> </Command> <Command Opcode = "1" Name="Send"> <Arguments> <Arg Key = "Channel" Value = "1"/> <Arg Key = "Msg" Value = "1"/> </Arguments> </Command>.7 פקודתעצירה (Opcode=9) ללא ארגומנטים. עוצרתאתפעולת ה- script ומחזירה אותו למצב המתנה. <Command Opcode = "9" Name="Halt"> <Arguments> </Arguments> </Command> 14
ד. הוספת פקודות חדשות עלמנתלהוסיףלחבילהפקודותנוספות, רצוי תחילה להבין את אופןפעולת הפרסר. הסברכלליעלפעולת הParser - ParseMainCommandManager פונקציה זונקראתעםיצירת הTHREAD, והיא אחראית ליצוראתאובייקט מנהל הפקודות הראשי שבסופו של דבריוכנסל-.CScenarioManager הפונקציה מאתרת בקובץ ה- XML אתה- Node הראשי Type="Main">),(<Commands ומעבירה אותו לפונקציהParseCommandManager. פונקציהזוהיאהיחידהשאינהסטטיתמביןכל הפונקציות שמבצעות.Parse ParseCommandManager יוצרת אובייקט מסוג CCommandManager ומחזירה אליו מצביע. הפונקציה עוברתעלכל הבנים ב- Node ועבורכלאחדמהםקוראתלפונקציה.ParseCommand למחלקתCScriptParser ישנהרשימה של פרסרים ספציפיים של פקודות.(OMCollection<CSpecificCommandParser*>) עם אתחול המחלקה, בבנאי שלה מוכנסים 15
לרשימהזו 6 פרסריםעבור 6 הפקודות שסופקו עם ה-.MesSim במידהוסט הפקודות הראשוני הורחב, באחריות המשתמש להכניספרסריםנוספיםעבור הפקודות החדשות לרשימהזולפני הפעלת הTHREAD (יוסברבהמשך). רשימת הפרסרים הזו מועברת לפקודתParseCommandManager כפרמטר, והיא מועברת כפרמטרגםלכל הפונקציות בהן היא משתמשת בהמשך תרשים הזרימה. ParseCommand פונקציה זובודקתאתה- Node שהוכנס לה, ומנתבת אותו לפרסר הפקודה הרלוונטי לטובתפענוח (ע"פ ה- Opcode של הפקודה המופיע ב-.(Node הפונקציה מבצעת מעבר עלרשימת הפרסרים הספציפיים,(OMCollection<CSpecificCommandParser*>) שכזכור הוכנסה כפרמטר. עבורכל אובייקטCSpecificParser ברשימהנבדקת התאמה של ערך החזרה של הפונקציה GetAttributeName של אותופרסר ספציפי ל- Opcode שלה-.Node במידהונמצאת התאמה ה- Node מועבר לפרסר הספציפי המתאים לטובתפענוח ) ע"יקריאהלפונקציה.(ParseSpecificCommand ParseArguments מפענחת Node מהצורה -,<Arguments> ומחזירהרשימה של הארגומנטים המפוענחים. <Arg Key = "StringForKey" Value = כזה- Node מפענחת ParseArgument "StringForValue"/> ויוצרת ממנו ארגומנט מפוענח SParserArgument) מבנה המכיל מחרוזת ל- Key ומחרוזת ל-.(Value דוגמת מחלקה לפענוחפקודה- (CSendParser) פונקציתCSendParser::GetAttributeName מחזירה את Opcode הפקודה (1). פונקציתCSendParser::ParseSpecificCommand יוצרת אובייקט חדש. הפונקציה מוצאת אתהבן< Arguments > CSendMessageCommand ומעבירה אותו לפונקציה הסטטית ParseArguments ע"מלקבל את רשימת הארגומנטים. אחר כךעוברתעל הרשימה שהתקבלה, ומציבהלאובייקט החדש את הערכים הרלוונטיים (במקרהזהרק 2 ארגומנטים מספרהערוץומספרOpcode ההודעה הנדרשת לשליחה). 16
פענוח פקודת תנאי: ParseConditions מפענחת Node מהצורה <Conditions> ויוצרתרשימה של תנאים (אובייקט.(TMessageCollection דוגמת מחלקה לפענוחפקודת תנאי CIfParser הפונקציה CIfParser::ParseSpecificCommand יוצרת אובייקט CIfCommand חדש. הפונקציהקוראתלפונקציה הסטטית ParseArguments ע"מלקבלאתרשימת הארגומנטים, ואחרכךעוברתעל הרשימה הזו, ומציבהלאובייקט החדש את הערכים הרלוונטיים (מספר ערוץ). מוצאתאתהבן< Conditions > ומעבירה אותו לפונקציה הסטטית.ParseConditions את רשימת התנאים שהתקבלה מציבה באובייקט הפלט. מוצאתאתהבן<" Type="True <Commands ומעבירה אותו לפונקציה הסטטית ParseCommandManager פונקציה זו מפענחת את מנהל הפקודות הפנימי, כמו שהיא מבצעת פענוח של כל מנהל פקודות! מנהל הפקודות שהוחזר מוכנס לאובייקט הפלט (בהרצת התרחיש, מנהל הפקודות הזה ייקראאם התנאי התקיים). באופןדומה מפענחת אתהבן<" Type="True <Commands ומציבהלאובייקט הפלט. 17
פענוח פקודות חדשות כזכורפונקציתParseCommand מבצעת מעבר עלרשימת הפרסרים הספציפיים (OMCollection<CSpecificCommandParser*>) ומחפשת התאמה של ערך החזרה של הפונקציה.Node שלה- Opcode של אחד הפרסר ספציפיים ל- GetAttributeName במידהוהוספו פקודות נוספותלחבילה, באחריות המשתמש להוסיףפרסר ספציפי של כלאחת מהפקודות החדשות לאובייקט ה- CScriptParser (יוסבר בהמשך). אותו פרסר שהוסף ע"י המשתמש יופעלכאשריופיעבקובץ הקלט Node עםאותוה-,Opcode ותבוצעפנייהלפונקציה החדשה שהוגדרהע"י המשתמש ע"מלפענחאתאותוה-.Node 18
הרחבת מכלול הפקודות עלמנתלהרחיבאת מכלול הפקודות הראשוני שסופק עם החבילה ישלממש 2 מחלקות חדשות: מחלקהראשונה אשר תייצג את הפקודה עצמה. מחלקהזו תירש מהמחלקה האבסטרקטית CCommand (בדומהלפקודות הקיימות היורשות ממחלקה זו). במחלקהזוישלממש את הפונקציה Run אשרתבצעאתהפקודהעצמה (פונקציתRun תקראע"יCCommandManager במקומותבהם הפקודה החדשה מופיעה בקובץ הקלט). ערךהחזרהשלפונקציהצריךלהיותtrue במידהולאחרריצת הפקודה ישלהמשיךלפקודה הבאהבתרחיש, או false אםכתוצאה מריצת פקודה זוישלסיים את התרחיש. מחלקה שנייה תהיה אחראית ליצור אובייקט חדש מהטיפוס הראשון. מחלקהזו תירש מהמחלקה.CSpecificCommandParser במחלקהזוישלממש את הפונקציות הוירטואליות: (1 (2 א. פונקצית GetAttributeName צריכהלהחזיר מספר השווה למספר שהוחלט עבור הפקודה החדשה המספר שיופיע בקובץ הקלט (מספר שאינו קייםכברעבורפקודה אחרת). בעתפענוחקובץ הקלט ע"י המחלקה,CScriptParser במידהונתקליםבמס' פקודה לא מוכר, מבוצע חיפוש במערך הפרסרים הספציפיים אחריפרסר שפונקצית GetAttributeName מחזירה את אותו מספר הפקודה החדש. ב. פונקצית ParseSpecificCommand המבצעת את התרגום עצמוומחזירהבסופו של דבר מצביעלפקודה שיוצרה. פונקציה זונקראתע"י אובייקט ה- CScriptParser ומקבלתכפרמטר אובייקט מסוג TinyXmlNode (המכילבתוכואתכלל השורות הרלוונטיות לפענוחבקובץ ה-,(XML ופרמטר המייצג את אוסף הפרסרים הספציפיים המוכרים (לטובתיכולת קינון פקודות). במימוש הפונקציה ניתןואףרצוילהשתמש בכל הפונקציות הסטטיות של המחלקה CScriptParser (ראה תחילת הפרק). ניתןלהשתמשבמחלקה שיורשת מ- CCommandParams עלמנתלאחסן את הפרמטרים של הפקודה החדשה. (3 בעתצריכת החבילה (במידהובוצעה הרחבה לפקודות חדשות), ישליצור אובייקט עבורכלאחד מהפרסרים הספציפיים, ולהוסיףכלאחדמהםלאובייקט של המחלקה CScriptParser (שלב מספר 1 בפרקב') ע"י הפוקנציה:.CScriptParser::AddSpecificCommandParser (כלל אובייקטי הפרסרים הספציפיים שנוצרו והוספול- CScriptParser יימחקו ב- d'tor של CScriptParser ולכן אין צורךלשחרר את הזיכרון שלהם מחוץ למחלקהזו). דוגמאלמחלקה היורשת מ- CSpecificCommandParser ומפענחתפקודה הדומה לפקודתIf : class CNewIfCommand : public CCommand { 19
} class CNewIfCommandParams : public CCommandParams { } class CNewSendParser : public CSpecificCommandParser { int GetAttributeName() { //this is the opcode of the command in the input xml script file return 15; } CCommand* ParseSpecificCommand(TiXmlNode * pcifcommandnode, const OMCollection<CSpecificCommandParser*> & cspecificparsers) { } } //parse arguments use the static function CScriptParser::TParserArguments tparserarguments; CScriptParser::ParseArguments(pcIfCommandNode->FirstChild(ELEMENT_ARGUMENTS),tParserArguments); //retreive arguments into the fields CScriptParser::SParserArgument sargument; int nchannel=0; for (unsigned int i=0; i < tparserarguments.getcount() ; i++){ sargument=tparserarguments.getat(i); if ("Channel1"==sArgument.Key) { nchannel = atoi(sargument.value); } } //parse conditions for the command use the static function TMessageCollection * ptconditions = CScriptParser::ParseConditions( pcifcommandnode->firstchild(element_conditions_collection)); //parse TRUE command manger - use the static function TiXmlNode *pcxmlnode = pcifcommandnode->firstchild(element_command_manager); CCommandManager *pctruecommandmanager=pctruecommandmanager= CScriptParser::ParseCommandManager(pcXmlNode,cSpecificParsers); //create the returned object return new CNewIfCommand(new CNewIfCommandParams(ptConditions,pcTrueCommandManager,nChannel)); 20
ה. רתמת בדיקות עם החבילה סופקה גםרתמתבדיקות. רתמת הבדיקות משמשת דוגמא חיה לתוכנית הצורכת את החבילהומשתמשתברוביכולותיה, ובעתיד תוכל לשמש תשתית לבדיקת החבילה במידהוזו תעבור שינוייםנוספים. רתמת הבדיקות סופקה כפרויקטRhapsody נפרד הצורך את פרויקט ה-.MesSim הרתמה מבצעתבדיקותעלפרוטוקול ה-.TCP להלן המחלקות העיקריות: יחידת הבדיקה מאפשרת עבודהבאחד מ- 3 מודי פעולה: 1) עבודהעם תרחיש פענוחע"יפרסר: פונקצייתStartScenarioManagerWithParser מריצהאתיחידת הבדיקה בצורה מלאה. לטובת הפעלת פונקציהזוישליצורקובץxml ממנו יפוענחו הפקודות, ולהעביר את ה- path אליו כפרמטר. 2) עבודהידנית: פונקצייתStartManually מריצהאתיחידת הבדיקה בצורהידנית: ע"י הדפסות למסך, המשתמש מקליד בזמן אמת את מספרי ההודעות הנדרשות למשלוח. 3) עבודהעם תרחיש יצירת תרחיש ידנית: פונקצייתCreateCommandManager מקבלת כפרמטר מצביע ל- CCommand ומייצרתCCommandManager חדש עם הפקודה הזו (במידהונוצרכבר, CCommandManager הפונקציה מוסיפה את הפקודה החדשה לסופו). בהמשך 21
ניתןלהריץ תרחיש עלה- CCommandManager הזה, ללאצורךב- Parser ע"י שימושבפונקציה הבאה: פונקצייתStartScenarioManager מריצהאתיחידת הבדיקה ע"פ התרחיש שהוכנסבצורהידנית. פונקציותנוספותלמחלקה: AddListeners() מוסיפה מאזין מסוג CMyMessageListener לכלל ההודעות (המאזין מדפיסלמסךכל הודעה שמתקבלת ואתכלל השדות שלה), ומאזיןCMyChannelListener לערוץ (המאזין מדפיס למסךכל שינוי שמתרחש בערוץ). Add/RemoveMessageListener מקבלת כפרמטר מצביע למאזיןלהודעהורושמת/ מסירה אותו מהאזנה לכלל ההודעות (תוכניתבדיקות היחידה משתמשת ביכולתזובהמשך). 22
הרצת תוכנית הבדיקה פרויקט ה- Rhapsody של רתמת הבדיקות ניתןלהרצה. עםהרצתהקובץישלפעולע"פההוראותהמופיעותעלגביהמסך- בחירתסוגאלמנט הבדיקה:, TCPServer/TCPClient ובחירתמודהריצה: ידני/ ע"פתרחישדינאמי (באופציהזו ישלהכניסבהמשךאתכתובתקובץה- xml עלפיוישלעבוד). לצורךבדיקה, ניתןלפתוחאתקובץ ההרצה של הפרויקט פעמייםולבחורלרוץבצדכ- :TCPServer ובצד השני לרוץכ-,TCPClient ניתךלהשתמשב- 2 קבציה- xml, שסופקועםרתמתהבדיקות, המדמיםתוכניתבהתהליך אחדמנסהלעשות login לתהליךשניע"יהקשתשםמשתמשוסיסמא. התהליךהשנימצפה לקבלשםמשתמש 99 וסיסמא 123, כלעודלאהתקבל שולחהודעתNAK. לאחרביצוע login מוצלח, התהליכיםמחליפיםביניהםהודעות. (בצילומיהמסךצדשמאלהואהשרת, וצד ימיןהואהתהליךהשנישמנסהלהתחברולבצע :(login 23
ו. בדיקותיחידה סטבדיקותיחידה סופק בפרויקטרתמת הבדיקות ומכיל בדיקות מופרטות לכל פקודה ולפרסר. הבדיקות מכילות בדיקותלמקרי קיצון כגון: שליחת הודעה שלא קיימת. בדיקת תרחיש ארוך ועמוס. תנאי הכולל מספרי הודעות שלא קיימים. המתנהעל 0 אולחילופיןעלMaxInt מילי-שניות. מרביתיחידות הבדיקה משתמשות במחלקותCTcpServer, CTcpClient עליהן הוסבר בסעיף הקודם עלמנתלפתוח את ערוץ התקשורת דרכו מבוצעות הבדיקות. המחלקהCRunTests אחראיתליצוראתכלל הבדיקות ולהריץ אותן. הריצה מבוצעת ע"יקריאה לפונקציתRun המקבלת כפרמטראתכתובת ה- IP עליה תבוצע הבדיקה (עדיפותלהכניסאת כתובת ה-.(LocalIP בדיקת היחידה של הParser מצפה למצוא 9 קבציxml לבדיקהאשר סופקו עם החבילה בתיקיה. O:\parseChecks 24