DCSIMG
February 2012 - Posts - הבלוג של צביקה פאר

הבלוג של צביקה פאר

בבלוג זה אני אנסה לשתף את הקוראים בחקירת מגוון טכנולוגיות נפוצות פחות או יותר

February 2012 - Posts

Frustum Culling

אחד האתגרים בתכנות מנוע תלת מימד זה בנוסף ל לדעת מה להכניס לכרטיס מסך זה לדעת מה לא להכניס לכרטיס המסך.

אל אף מהירותו וריבוי ה מעבדים ב GPU תחולת עיבוד גדולה תגרום להשהיות של ה Rendering thread כך שאיכות המוצג תיפגע עקב קצב רינדור נמוך (מספר frames לשנייה ).

אם נבחן scene מתוך ה Demo  של SubDMesh

 

4

נראה שלצייר Scene בודד לוקח כ 50mSec .

בGraph שה Pix  נותן לנו ניתן לראות היכן הרוב המאמץ והזמן עיבוד של ה GPU לוקח .כמובן בפקודת Render (מה שמיוצג על ידי החורים הלבנים )  בפרוט ה פקודות ניתן לראות את כמות ה Vertex ים הגדולה שכל פקודת Draw צריכה לטפל כמובן שהכמות מוכפלת עקב זה שה Demo משתמש ב Tesselation.

בכל מקרה בחרתי ב Patch divisions נמוך יחסית על מנת לא  להכביד על ה GPUעל ידי יצירת הרבה Vertex חדשים כתוצאה מה Tessellation.

2

 

3

ב Scene שהודגם היה סך הכל חייל אחד מסכן שאני חושב שאפילו משרת אומנם בגבורה אבל בקריה. ב Sceneים  אמיתיים כמות הMeshs בהרבה יותר גדולה  .

כל Vertex , index שנכנסים לכרטיס מסך עוברים עיבוד דרך כל ה Shaders הפעילים ברגע הזנתם , רק בסוף ה ה Pipeline מתבצע ה Cliping של ה Data שאיננו מוצג .

11

ה RasterizerState קוצץ את כל ה pixels שלא נכנסים לאותו מלבן ש החלון שמוצג כלומר אם יש לנו scene שמורכב מאצטדיון כדור רגל שלם וכרגע רואים במסך רק שחקן אחד , רק ה pixel של אותו שחקן יגיעו ל pixel shader .

ה Z- test לסוגיהם מתבצעים בשלב יותר מאוחר כלומר אם אני מרדנר קריין חדשות מאחורי שולחן ל pixel shader יגיעו גם ה pixel ים של ה שולחן וגם של המכנסים הקצרים שלו (במקרה הטוב ) שלא נראים כלל .

כאן ללא כל קשר לטכנולוגיה שבה משתמשים בא התפקיד של התכנת למנוע ככול הניתן הגעה של vertex ים ו index ים שלא יגיעו לידי רינדור .כמובן גם פעולות אילו אשר מתבצעות ב Cpu יש להן מחיר מבחינת ביצועים ,אולי חלק מהן ניתן לבצע ב Thread אחר בכל אופן לבדוק בכל Frame כל  vertex לפני שהוא מגיע לכרטיס מסך האם הוא Visibility זה דבר בלתי ישים לחלוטין !!!

 

LOD

מכיוון שאין שום סיבה שבתחום של 3X3 פיקסלים בו אמור להופיע מטוס קרב מרוחק ירונדר F22 עם מיליון Vertex ים  נוצר מנגנון של LOD שמהותו לכל mesh שיכול להיות מרונדר ישנם מספר גרסאות שנטענות לכרטיס מסך בהתאם לקירבה של ה mesh לצופה .

14

ב Direct3d11 ניתן לבצע lod  יותר חלק באמצעות tesselation .

 

View Frustum Culling

viewing frustum הינו החלק בעולם שהינו נראה על ידי המצלמה .

17

ה View frustum מורכב מ 6 משטחים כאשר כל מה שנמצא בתוכו הינו נראה על ידי המצלמה.

המטרה של הבדיקה הינה לראות האם mesh או חלק מ mesh נמצא בתוך ה View frustum ואם כן אז להזרים את ה mesh לכרטיס מסך.

ישנה דוגמא מאוד טובה ומאוד מאוד מאוד פשוטה אשר נלקחה מקורס תכנות גרפיקה ממוחשבת לתלמידי מדעי החברה באחת מהמכללות .את הדוגמא אפשר למצא כאן.

הדוגמא מבצעת 3 פעולות מרכזיות במטרה להחליט אם אוביקט נימצא ב View frusum

1.הדוגמא מגדירה את 6 ה planes שמרכיבים את הצלעות של ה frustum של המצלמה .הקוד הינו קוד סטנדרטי ביותר שאין צורך להמציאו .

2.עבור כל סוג של אוביקט תלת מימדי הדוגמא מגדירה פונקציה שיודעת לחשב האם האוביקט נחתך \ מוכל בתוך ה frustum או נמצא מחוצה לו

דוגמא לפונקציה כזו עבור כדור הינה :

16

3.הדוגמא מרנדרת רק את האובייקטים שנמצאים בתוך ה View frustum או נחתכים אייתו.

מכיוון ש mesh הינו צורה יותר מורכבת מכדור או תיבה ואם נתחיל לבדוק כל vertex ב mesh כל Frame הcpu יחנק לנו בד"כ בונים תיבה חוסמת ל Mesh ובודקים האם היא נחתכת או נמצאת בתוך ה View frustum .

18

החישוב של ה מלבן החוסם את ה mesh יכול להיות מבוצע על ידי שימוש ב פונקציה D3DXComputeBoundingBox שהינה חלק מה Dx sdk .

לפני שנים רבות הייתה המלצה להשתמש בכדור חוסם במקום במלבן חוסם עקב זה שמספר הבדיקות של מלבן חוסם גדול פי 6 מאשר כדור ,היום בcpu ים המודרניים גם בדיקה של מאות מלבנים חוסמים לא מורגשת .

ניתן לבצע אופטימיזציות על החישוב ,רוב האופטמיזציות מכוונות למצבים בהם רק חלק מה mesh מופיע ב frustum . ניתן לחלק את ה mesh ל Quad trees (ב gpu gems בפרק של הterrain יש הסבר איך לעשות את זה )  כאשר מרנדרים terrain הדבר מאוד חיוני , יש מאות אלגוריתמים סטנדרטים לTerrain (דוגמא) לגבי mesh ים בודדים כדוגמת מטוס לא בטוח שהטרחה משתלמת.

כאשר ה meshהינו מסוג Hierarchical Bones Mesh ניתן לבצע את החישוב של איזה חלק מה mesh יכנס ל frustum באופן רקורסיבי .

20

 

וכל זה הקדמה לבאג שהופיע אצלנו לפני כשבועיים :

כאשר היו מטיילים עם המצלמה לאורך אחד הצירים היו מבחינים במעין קפיצה של mesh ים קרובים כאשר ה Mesh היה אמור להיכנס לתמונה ,מאין השהייה של כמה מאות מילי רק ל Mesh ואז בבת אחת חלק ממנו הופיע הוא המשיך להיכנס חלק לScene באופן חלק .

לאחר חיפוש הסתבר לנו הדבר הבא :

מכיוון שה Mesh  היה מרונדר באמצעות Tessellation map ו Tessellation maps בניגוד לכל ה Maps לסוגיהם (Light , hights וכד) בעל יכולת הרחבה של ה mesh מעבר למימדים שלו . ההרחבה בוצעה רק בGPU וכאשר חישבנו את ה frustum culling ב CPU הדרנו את הMesh למרות שחלק ממנו כבר היה צריך להיות בתמונה .

הפתרון מבחינתנו היה לתת למנגנון ה frustum culling מקדם עבור כל mesh שבאמצעות הfrustum מאין מוגדל עבור mesh ים מסוימים בקרבה ל מסך .

אם נביט בתמונה הבאה אפשר לראות שיש חלק שלם של מים ליד הגלגלים שנוצר על ידי מנגנון ה Tessellation ב gpu  ואיננו נכלל בחישוב של ה frustum culling המתבצע ב cpu .

15

Nosql document DB

הדרישה

באחד מהפרויקטים עליהם עבדתי בעת האחרונה היינו צריכים לבצע ארכיטקטורה של שכבת DAL עבור אפליקציה שמכילה מספר רב של סוגים של ישויות מידע שונות ביילתי קשורות או תלויות זה בזו כמעט לחלוטין .כלומר לפעולות join לסוגיהן בין סוגי ישויות המידע אין קשר לוגי .

על ישויות המידע השונות יש לבצע פעולות Cruds כתיבה חיפוש עידכון ממחיקה , Full text search וכד

כמות הפריטים בכל רשימה של ישויות מידע נעה בין בודדים לאלפיים ,כאשר דרישה נוספת זה שהסכמה של ישות מידע הינה דבר דינמי שיכול להשתנות עם הזמן .

ישויות המידע כתובות כ Poco objects בלתי תלויות כאשר ישות מידע יכול להיות מורכבת מ Class ראשי שמכיל מספר  Classי משנה.

6

חשבנו לבחון אפשרות של שימוש ב NoSql Database מסוג Document כ Repository משום ששימוש ב RDBMS גם מצריך הרבה עבודה של יצירת הרבה מאוד טבלאות , שינוי סכמות דחופות  בניית שיכבת dal מורכבת ואין הנאה מהיתרונות של RDBMS הן בגלל גודל יחסית קטן של הטבלאות והן בגלל שאין קשר בין הטבלאות .

(אפשרות נוספת שחשבנו עליה היתה שימוש ב code first של אחד מה Frameworks )

ה NoSql Document Db שבחרנו לבחון הינו RavenDB .

RavenDB  הינו NoSql DataBase שכתוב ב net. ששומר מסמכים בתצורת JSON documents שתומך בטרנזקציות , RavenDB רץ כServer כאשר הממשק אליו הינו באמצעות שאילתות Linq . ניתן למצא פרטים על RavenDB הן באתר ,מאמר ב msdn וגם בסדרת פוסטים בנושא כאן וגם כאן.

היתרונות

  1. פשטות התממשקות וכתיבת שיכבת הDal , Interface חושף  RavenDB טוב שעונה על ההגדרה של Interface אידיאלי בחלק של פשוט מאוד לעשות איתו דברים פשוטים בחלק של אפשרי לעשות איתו דברים מורכבים אני לא כל כך הסתדרתי אולי זה נובע מחוסר תעוד .
  2. פשטות הפעלה ותמיכה ב DB .
  3. ביצועים מעולים בכל הפעולות שניסינו גם בכמות מידע שגדולה בכמה סדרי גודל ממה שאנו צריכים .
  4. תמיכה מעולה של החברה גם בstackoverflow וגם בקבוצת הדיון .
  5. מחיר יחסית זול ל RDBMS ים

6.ממשק ניהול נוח .המאפשר לבצע גיבויים לראות את הלוג , למחוק , לראות את כל הdocuments ב Database , ניהול index ים .

דוגמא למסך מתוך ממשק הניהול בו ניתן לראות את ה Documents השמורים , לבצע חיפוש להוסיף ידנית .

1_thumb2

החסרונות:

  1. כלי ניהול כדוגמת גיבויים אוטומטיים , שאילתות online לא מפותחים מספיק , למרות שלצרכים שלנו ניתן לתפור את הכלים די בקלות לפי הדוגמאות המסופקות עם החבילה .גיבויים ידניים המערכת מאפשרת לעשות באמצעות ממשק הניהול בקלות.
  2. אפשרות הרחבה לעתיד של ה Storage לצורכי BI לדוגמא על מנת לשמור את כל הפעמים שהליך רץ ותוצאת הריצה וחיפושים ותובנות ,הדבר אפשרי אבל מהר מאוד זה מתחיל להיות יותר מסובך מאשר שימוש ב sql db שמיועד למטרות אילו.
  3. חוסר תעוד רישמי למרות שאפשר למצא המון מזור באתרים כגון Stackoverflow וכד
  4. חוסר בדוגמאות (תמיד חסר לי אולי ב WCF נתקלתי בכמות מספקת ).
  5. נושא ה Security לא מפותח מספיק לצרכינו לטעמי .
  6. תומך בעיקר בעולם ה net. דבר שבעיתי אצלנו בעיקר באפליקציות שכתובות בpython , java , php .
  7. חסרה יכולת לשמור ערכים עם Referance מעגלי של אוביקט אחד לשני ובחזרה .

התקנה והרצה

לאחר שהורדנו את החבילה מהמרשתת על מנת להפעיל את הdb זה להפעיל את ה start batch file (דבר שפותח אוטומטית את ממשק הניהול הסילוורלאיטי ).

על מנת להתחיל לפתח יש לחבר מספר assemblies ל project להעתיק מספר שורות קוד ואתם בפנים .

8

Open DocumentStore and Session

5_thumb3

הממשק מול ה RavenDB מורכב שני אובייקטים מרכזיים :

ה Docuemnt store שמכיל את הממשק ברמה של הDataBase .הוא עוטף את אפשרויות היצירה שלה Session , התחברות ל Db instance

שמירה של אוביקט חדש

השמירה מעדכנת את ה ID של האוביקט הנשמר כאשר מבנה הid  אם הוא לא מסופק על ידי הצרכן מחולל  בtemplate שמורכב משם הclass עם סיומת רבים + ה id החדש .(המערכת יודעת מבחינת אנגלית להוסיף נכון את הסיומת )

7

אין צריך להגדיר ב DB את ה Scema של האובייקט או את התיקיות הכל מוגדר אוטומטית.

עדכון אוביקט

על מנת לעדכן אובייקט ב Database יש לבצע את פעולת הStore עם האוביקט המעודכן כאשר ה ID מכוון לID שאותו אנו רוצים לעדכן ,כל שינוי גירסה מעדכן את הערך של ה etag ואת זמן העדכון .

שאילתות ל DB

כאמור שאילתות מתבצעות תוך כדי שימוש ב Linq .

RavenDB עבור כל שאילתה בונה index דינמי בהתאם לתוכנה של השאילתה למטרת החזרת התשובה ,לשאילתות שכיחות RavenDB בונה index סטאטי שמתעדכן ב thread נפרד ,כאן יש עניין של נעילות אם רוצים לקבל את המידע המעודכן ביותר לאותו הרגע .

דוגמה לשאילתה :

12

ניתן להגדיר מראש את השאילתות אשר ישמרו באופן סטאטי כל עוד לא מוחקים אותן :

 

13

 

http://www.gamlor.info/wordpress/2011/07/ravendb-queries-and-indexes/

שונות

RavenDB תומך ב Attachments של קבצים בינארים (Blobs) כדוגמת קבצי תמונה או אודיו .

ניתן להרחיב את החבילה הביסיסית של RavenDB על ידי Plagins .

יש אפשרות לבחור מנוע אחסון כ Embedded ואז הקריאות ל DB מתבצעות באופן לוקלי ולא דרך ה network , כמו כן ניתן להגדיר שהנתונים ישמרו באופן ארעי בזיכרון RunInMemory = true

שיכבת ה DAL

המטרות שלנו בארכיטקרטורה של שיכבת ה Dal הינן:

  1. כמה שיותר Decoupling  משיכבת ה BLL כך שאם נצטרך מסיבה זו או אחרת להחליף את מנוע ה Storage הנגיעה בשיכבת ה BLL  תהייה מינימלית .
  2. אין צורך שהמערכת תוכל לתמוך בשני מנועי אחסון שונים בשינוי קונפיגורציה .
  3. זמן עבודה בערך כ 50 שעות (בחיים האמתיים זמן עבודה הינו אלמנט די מכריע בתכנון ארכיטקטורה )

עקב זה שלא נראה שיהיה צורך שהמערכת תוכל לתמוך ביותר ממנוע אחסון אחד שכבת ה Dal  יכולה להיות קונקרטית לטכנולוגיה ,וכאשר מסיבה זו או אחרת ירצו לתמוך במנוע חיפוש אחר יבצעו פעולת חציבה ורמונט כללי בassembly של הDAL.

Components diagram

5

ה Components מהם מורכבת שכבת ה Dal יכולים להיות מקובצים לפי מידת הקונקרטיות לטכנולוגיה כאילו שימחקו ויכתבו מחדש עכב שינוי טכנולוגית האחסון וכאילו שרק יעברו שינוי קל לצורך התאמתם לטכנולוגית אחסון אחרת.

השיכבה יכולה להיצרך ישירות או עלי ידי תפירתה בקלות כ Web Service.

Class diagram

6

 

בדיוק כפי שאני מעדיף ששכבת ה Dal תבצע שאילות SQL  או תקרא ל Sps ככה אני מעדיף ששאילתות הlinq יהיו חלק משיכבת ה Dal ול BLL יחשף interface לוגי .

 

10

 

9

הנושא של ארכיטקטורת ה Data containers נושא בחובו דילמה האם להשתמש באותם DataContainersלאורך כל השכבות מה Dal עד ל UI , מה שגורם להם:

  1. להיות עטופים בattributes שונים שכל אחד קשור לשיכבה אחרת.
  2. ,לממש interface ים משכבות שונות
  3. ולדאוג ל Convertors לדוגמא לשיכבה של ה Dal לא מתאים member מסוג Bitmap אלא מערך של Bytes .

אפשרות שניה עבור כל שיכבה לייצר DataContainers משל עצמה כאשר אפשרות זו נושאת בחיבה  עבודה של כתיבת Convertors שמציבים את הDataContainer של שיכבה אחת בשניה .

 

עבור כל DataEntity נכתב accessor קונקרטי שממש את ה interface הספציפי שהוגדר עבור אותו DataEntity .

פעולות משותפות הוגדרו בclass משותף

7

 

אלמנטים שמתפעלים את הRavenDB 

CustomSerializators – שממירם לנו אובייקטים שלא ניתן אוטומטית להמיר ל JSON

DbOperationsAccessor – מכיל פעולות כמו מחיקת DB , רפליקציה שממומשת על ידנו , יצירת DB

RavenDbSessionWrapper שעוטף לנו את הקריאה גם ל Document Store , ל Init פניה ל configuration להביא את ה port ושם ה DB

RavenDbDataIndex – הגדרות של index ים סטאטים

 

8

 

מסקנות

ראשית נחשפנו לטכנולוגיה של NoSql Document DB .

העמדנו מערכת בזמן קצר יחסית עם ביצועים משביעי רצון .

חסר לנו הקטע של Security כנראה שהתרגלתי לאושר של sql server וכד הן מבחינת אפשרויות הlogins וה roles דברים שהיו חסרים לי כאן .

חסרה לנו התובנה כיצד לבצע שאילתה שמכילה  Search על text למרות דוגמאות רבות לא הצלחנו בזאת .

Posted: Feb 22 2012, 11:14 PM by zvikapeer | with 2 comment(s)
תגים:,

Wcf UDP transport

בגירסה הבאה של .wcf  4.5 יכלל גם transport חדש שתפקידו לתמוך ב Udp .

הצורך ב udp transport  היה אצלנו כבר כמה שנים הסיבות לכך היו :

  1. אסינכרוניות מלאה אין מצב ל Dead locks  .
  2. תעבורת רשת יותר קטנה מאשר שאר ה transports  חשוב כאשר יש תעבורה גדולה ורוחב פס קטן .
  3. ביצועים :זמן שליחת , קבלת הודעה בהרבה יותר טוב משאר סוגי ה transports  .

בד"כ משתמשים ב udp transport במקרים בהם דרושה יכולת של שגר ושכח לדוגמא מקרר בסופר שמדווח כל פרק זמן על הסטאטוס שלו האם הדלתות פתוחות , הטמפרטורה וכד. לא מעניין את המקרר האם מישהו מאזין אליו , ולא מעניין את המאזין תוכן ההודעה הקודמת אם הוא כבר קיבל הודעה חדשה .

1

כאשר יש רק סוג אחד של הודעה שנשלחת באופן מחזורי ,אפשר לתהות מה ה Add in values ש Wcf נותן לנו על פני סתם המרה של האוביקט מידע למערך של bytes ושליחתו באמצעות udpclient .כאשר ה Contract נהייה מורכב ,ויש דרישות נוספות כגון security ,  Discovery , routing מתחילה להיות משמעות לשימוש ב wcf .

על מנת לספק את הדרישה הזו השתמשנו ב Udp transport שסופק כדוגמא ב sdk .הדוגמא מבחינת ה interface שהיא חושפת מאוד דומה לudp transport של 4.5 .

הדוגמא מספקת לנו udp transport channel כאשר ה Channel עצמו ממומש כ IInputChannel ו כ IOutputChannel.

 

2

 

3

החלקים המעניינים הינם כל הטיפול ב Socket ב Class ים האילו ב OutputChannel בפקודת Send אשר מבצעת סיראליזציה ל Message object וקוראת ל פקודת SendTo  של ה Socket

 

4

ב input channel המימוש קצת יותר  מורכב.

ב פקודת OnOpened של UdpChannelListener נוצר ונפתח udp socket עבור כל endpoint ש ה Service חושף.

לכל socket שנוצר מתחילים להאזין באופן אסינכרוני באמצעות הפונקציה ContinueReceiving .

 

6

כשמגיע הודעה חדשה ההודעה החדשה נדחפת לתוך Queue אשר תפקיד ה UdpInputChannel לקרא ממנו את ההודעות ולשלוח אותן ל Dispatcher .

בכוונה הדגשתי את הקטעים שקשורים ל udp socket שאר הקוד מטפל הן בהודעות סינכרוניות והן אסינכרוניות ומהווה בסיס מצוין לפי דעתי לכל צורך למימוש Transport channel החל מ Rs232 וכלה במנגנון שמפריח יונות דאר .

מכיוון שהדוגמה מתעסקת רק עם ה transport channel עדיין נשארים כל המנגנונים שלא קשורים באופן ישיר לתעבורה לדוגמא הEncoder ו הDecoder של ההודעה איזה encoder שרק תבחר או תממש כאן קביל, אפילו תקודד את ההודעה למורס אין שום קשר ל transport

5

ה encoder הינו class שיורש מ MessageEncoder ואפשר לצינו הן בקוד או בconfiguration .כאשר מגדירים את ה Binding בדוגמא משתמשים ב TextMessageEncodingBindingElement.

הדוגמא מדגימה איך ניתן לבנות את הChannel stack כך שיתקבלו תכונות שאינן אפשריות עקב האופי ה אסינכרוני של udp .

לדוגמא:

  • פעולה סינכרונית הClient קורא ל Server וממתין לקבלת תשובה .
  • ניהול wcf sessions
  • Reliable messages  - ה Client שולח הודעה ומחכה timeout עד שההודעה מתבצעת , יכול להיות מצב שה Client שלח את ההודעה כאשר ה Server בכלל למטה ואז כשהריצו את ה Server אם לא חלף לו ה timeout אז הפקודה תתבצע .

הDemo על מנת לתמוך בפעולות אילו מוסיף ל Channel stack  את ה ReliableSessionBindingElement.

על מנת לתמוך ב Duplex channels ה demo מוסיף ל Channel stack את ה CompositeDuplexBindingElement.

גם תמיכה ב טרנזקציות אפשרית והיא מודגמת באמצעות הDemo של TransactionMessagePropertyUdpTransport

תמיכה ב hosting  ב IIS מודגמת באמצעות ה Demo של UdpActivation.

אחד המקרים  שצריך את התכונות האילו ב udp transport הינה כאשר משתמשים בudp כ  Interoperability למערכת צד ג שהתעבורה אליה הינה udp והיא מתנהגת באופן סינכרוני .

יש לזכור שכאשר עובדים עם Relaible session מקבלים הפחתה ניכרת בביצועים .

Posted: Feb 18 2012, 09:56 PM by zvikapeer | with no comments
תגים:, ,

Creating WF workflow dynamically part 1

חלק ניכר מ Frameworks ,ארכיטקטורות  לסוגיהן כדוגמת MVVM , Spring  , MF ,  מגדירות מסגרת שהארכיטקטורה של המערכת צריכה להתיישר על פיה.

לדוגמא ב mvvm הקשר בין ה modal  ל view  צריך להיות דרך ה modalview ופנייה ישירה מהview ל modal  כדוגמת הירשמות לevent  של click  של button ומימוש ה event  ב xaml.cs  שוברת את הארכיטקטורה על אף שלכאורה הכול יעבוד.

לארכיטקטורה כזו מספר יתרונות שהמודגשת ביותר זו כמובן ה Decoupling  בין השכבות כך שבעולם אידיאלי ניתן יהיה לשנות את ה view  מבלי צורך לשנות את שיכבת ה UIL .בעולם האמיתי זה יותר בכיוון שלתכנת יהיה קצת יותר קל להבין את הקשר בין שיכבת ה UIל UILופחות מסובך לשנות את שיכבת הUIL  כך שתתאים ל view  החדש .  

אחד היתרונות שלפי דעתי יותר משמעוותים שארכיטקטורה כזו נותנת זה אותו השלד של ארכיטקטורה שרק צריך לעטוף  בשכבה כמה שיותר דקה של לוגיקה עסקית קונקרטית.

יותר פשוט לעשות ארכיטקטורה לפי כללים מסוימים לדוגמא בשכבת ui  המבוססת על mvvm  הארכיטקט יודע שהוא צריך להגדיר את ה modal  ודרך זרימת הcruds  אליו , הוא צריך להגדיר את הmodalview  ואת ה forms  ו ה user controls  של הView .

עדיין יש מרחב התקשקשות ואין שבלונה קבועה אבל זה עדיף בהרבה ממצב בו מתחילים ממצב נקי לגמרי בשיא ההתלהבות וחושבים על פיתוח כל מיני תשתיות גנריות שישמשו גם לעתיד וישום כל מיני patterns  תאורטיים   והמערכת יוצאת מסורבלת וכל מה שחושבים לגבי העתיד זה מתי ניתן לזרוק את המערכת ולכתוב הכול מחדש .

החסרונות של אותה ארכיטקטורה המבוססת על framework

גמישות – דברים לא מורכבים  בדרך כלל קל לעשותם עם אותו ה Framework  דברים מורכבים לפעמים ההשקעה שצריך במערכת שמפותחת באמצעות framework  גדולה אין ערוך מזו של עבודה ללא ה framework  .

לדוגמא : מימוש GraphView  של nodes  במקום TreeView בMVVM .במצב ערום ללא framework  הדבר מורכב אבל אפשרי , כאשר מנסים ליישם את זה באמצעות mvvm ומתחילים לחפש איך לרשת מ HierarchicalDataTemplate , איך לשנות שיהיה אפשר שchild  יהיה משותף לכמה אבות כמות ההתעסקות בכיפוף הטכנולוגיה הינה אדירה. עד כי נראה כי ניתן להחליף לנזירים ממגדלי הנוי את העיסוק היום יומי .

אי תמיכה – מחר מיקרוסופט או חברה אחרת  יכולים להחליט שהם מזיזים הצידה טכנולוגיה זו או אחרת ואז מה ? קוד לוגי שאיינו מסתמך על טכנולוגיה בהרבה יותר פשוט להעביר לדוגמא מ C#  ל java  מאשר workflow  של wf  לאיזשהיא טכנולוגיה מקבילה או לקוד .

Technology awareness – עצם הקישור לטכנולוגיה מגבילה למה שהטכנולוגיה יכולה להציעה לדוגמא אם השתמשנו ב mvvm שקשור ל wpf  יהיה קשה לנו להכניס טופס ישן מבוסס על winforms.

מוטיבציה

ברגעי משבר שאתה מתחרט שלא שמעת בקול של הוריך שרצו שתהייה מציל בחוף שרתון ואתה התעקשת ללכת לעבוד בהי טק כי יש שמה מכונות קפה שוות בא לך לעשות double click  על כפתור בEditor של ה  טופס וליצור event  ולממש שם את התוכן שלך בקצרה במקום להתחיל להגדיר commands  ו bindings  ו לשים את הcommand  ב context .

שימוש ב Framework כזה או אחר דומה לטיול מאורגן לחו"ל מאוד נוח רואים יותר דברים לא מבזבזים זמן על התעסקות בדברים אדמינסטרטיבים אבל בכל זאת מפסידים משהו מהחוויה.

WF Framework

WF  לא נותן יכולת טכנולוגית שלא היתה קיימת קודם או שהיה צריך להתאמץ מאוד על מנת לישמה נהפוך הוא הרבה מאוד פעמים ליישם בקוד דברים יותר פשוט מאשר מwf .

WF  נותן מסגרת למימוש תהליך עסקי , מה שחוסך צורך בהגדרת ארכיטקטורה אם אפשר להתיישר לפי wf  .

אחד השימושים שאנו משתמשים בטכנולוגיה זו הינה יצירה דינמית של bl workflows .

דוגמא דומה לישום שלנו הינה מכונה שיודעת להכין עוגות לבד בהתאם למתכון שהוזן על ידי המשתמש . (כמובן שHighlight  שלה זה עוגת שוקולד עם ספותה שחורה ).

השף מגדיר באמצעות מסך גרפי את התהליך של אפיית העוגה הוא מגדיר חומרים לעוגה , תהליכים באפיה בדיקה עם קיסם עץ אוטומטי האם היא אפויה וכד.בסיום התכנון השף שומר את התוכנית בבסיס נתונים ,התוכנית תשמש במכונה לצורך הרצת תהליך האפייה .

למרות ש wf  תומך ומעודד שימוש בDesigner שלו לצרכי בניית ה Wf  (דוגמא DesignerRehosting  בsdk )  מנסיון שלנו לא טוב לנסות קשור בין ה Ui  של ה wf editor  ל UI  של משתמש הקצה  אלא להתאים את  הui לuse case  של המשתמש ולא לטכנולוגיה של שיכבת הBLL   .כמו שלא ניתן לשף מסך לעשות joins  בין טבלאות למרות שאפשר להציג את זה בצורה מאוד נוחה למשתמש .

מה גם שהeditor של wf עדיין לא מפותח דיו ואיננו מאפשר קסטומיזציה טובה .לדוגמא בגירסה הנוכחית הוא אינו תומך בcustom activity designer  באפשרות להכניס control  אחר מאשר edit box  לדוגמא combobox  ולאפיין את הקישור אליו.(כפי שהבנתי בגרסה הבא מיקרוסופט אפשרו את היכולת הזו ).

המערכת אצל המשתמש מפיקה את ה workflow באופן דינמי ושומרת אותו בבסיס נתונים.

יתרונות של הארכיטקטורה הינן :

הארכיטקטורה מפרידה תהליך מאוד מורכב של קליטת נתונים ממפעיל והפעלת מכונה עם אותם נתונים לשני חלקים :

חלק סטאטי שהמכונה בזמנה החופשי מייצרת workflow על פי הנתונים של המפעיל . כאשר הworkflow שמיוצר הינו מינימליסטי ומאוד קונקרטי לטובת תהליך אחד  בלבד.

חלק דינמי שהוא יותר real time שמריץ את הworkflow שיוצר מראש .

תהליך דביגאבילי :

ניתן להשתמש בכלים שה wf חושף בצורה מיטבית הן להציג את התוכנית לפני הריצה והן תוך כדי ריצה לראות את התקדמות שלה . 

7

לWF ישנן יכולות tracking מאוד טובות שהינן Build in .

שימוש ביכולות הWF מובנות של

כדוגמת :

 Cancelation ‏ ו ב Compensation

אם באמצע תהליך אפיית העוגה מסיבה מסוימת מחליט האופה לבטל את התהליך או שלוגיקת התוכנית עצמה קוראת ל Cancel

9

יכולות אילו נותנות אפשרות להגדיר Activities אשר ירוצו הן במקרה שActivity הסתיים בהצלחה ובהמשך הריצה נקרא Cancel  ואז צריך מאין להחזיר את הגלגל אחורה ולבצע פעולה מנוגדת לפעולה שכבר הסתימה בהצלחה כמו אחרי הזמנת כרטיסי טיסה אם המערכת מבטלת את התהליך כתוצאה מאי אישור של מנהל אזי יש לבצע פעולת פיצוי של ביטול ההזמנה .

ונותנת גם אפשרות להגדיר ל activity  סוג של תהליך שיקרה כאשר הcancel נקרא כאשר הוא במהלך הריצה ולא השלים את עבודתו .

 

תשתיות הWF נותנות יכולות לכלול השהיית התהליך והמשכתו  בשלב מאוחר יותר .

טרנזיטיביות זה לא בדיוק תכונה נתמכת במערכת עקב החוק השני של טרמומכניקה שקצת קשה להפריד חזרה את הביצים , קמח והגזר מהתערובת של העוגה אם לא הצלחנו בתהליך הדלקת התנור .

Use case

1

המפעיל של המכונה טוען את הקובץ הworkfow נטען ויכול להריצו ביחד עם פעולות כמו לבטל הרצה , לראות את התוכנית של האפייה וכד

2

 

אבני הבניין העיקריות של הארכיטקטורה

3

 

Class diagram

Custom activities

אילו Activities קונקרטיים שמהווים את אבני הבניין לWorkflow שיחולל אוטומטית.

4

הערות

1.הDesigners של ה Activities כתובים ב Wpf ,הבעיה המרכזית איתם בגירסה 4.0 הינה העדר יכולת להכיל controls לעריכת Arguments או variables שאינם EditBox ואז לדברים מורכבים כגון list של אלמנטים בלתי אפשרי לתת אפשרות עריכה .

למיקרוסופט יש פיתרון פנימי לבעיה הזו הוא משמש לדוגמא ב Send activity לצורך מילוי השדות של הפרמטרים , לא הצלחתי להעתיק את הפתרון שלהם לאפליקציה שלי.

ניתן לקשור לXaml בBinding ערכים של modalview אשר ישמשו בזמן debuging להצגה אקטיבית של state של הריצה .לדוגמא: במקום הDesigner הסטנדרטי של Delay activity ניתן להחליפו בDesigner שמראה זמן יורד תוך כדי ריצה .

2.על מנת לבצע פעולה ממושכת כמו דימוי האפייה כמומלץ אנו לא ממשים בתוך ה Bakeing activity סוג של while loop  עם sleep ובדיקה מחזורית,אלא משתמשים בextention .

כאשר הactivity מסיים את פעולות האתחול שלו הוא נעצר בbookmark ואז extention  ששייך לו מבצע את הבדיקות וכאשר התנאי מתקיים הוא משחרר את הbookmark וה Activity יכול להמשיך בעבודתו .

הדבר נועד לאפשר את תהליכי ה Cancelation וכד

8

הקוד:

5

6

 

NDIS filter driver part 3

ברוכים הבאים לפרק השלישי של הטרילוגיה שעוסקת בשינוי ה data packet שזורם לרשת או ממנה באמצעות Ndis filter driver.

לצערי בגירסה הנוכחית של NDIS  עדיין  אין תמיכה בשאילתות Linq  ואו בReflection לכן את העבודה שלה parsing של ה NBL עלינו לבצע באמצעות פקודות  וmacros  של wdm  ו ndis .

מבחינת תוכן לוגי ה NBL בהודעת udp בנוי בצורה הבאה :

4

חלק של Header הודעת Ethernet

נציג של שיכבת הקו בosi .

חלק שזה מכיל את ה Mac  של כתובת המקור ,

ה Mac של כתובת היעד .

ופרוטוקול המידע של ההודעה לדוגמא: ip4 ,ip6 , arp  , Icmp וכד

12

חלק של Header הודעת Ip4

נציג של שיכבת הרשת ב osi

מכיל את הנתונים הבאים :

גרסת הפרוטוקול ,

אורח הפתיח ,

סוג השרות ,

הגודל הכולל של החבילה ,

מספר הזיהוי של החבילה שמשמש כאשר הודעה מורכבת ממספר חלקים

(TTL - Time To Live).

הפרוטוקול שבאמצעותו מעבירים את המידע לדוגמא : tcp , udp ,

כתובות ip של שולח ההודעה וכתובת ip של הנמען הסופי .

10

חלק של Header  הודעת Udp.

נציג שכבת ה תעבורה ב OSI

מכיל את פורט ה מקור ופורט היעד אורך החבילה וה Checksum .

15

payload , Data

המידע עצמו שנשלח או התקבל ברשת לדוגמא הודעות soap , http ,smtp וכד

11

מבנה המידע בהודעת udp

באיור מוצג structs שמרכיבים את מבנה של הHeader המבנה מתאים להודעת udp בלבד ,כל הודעה אחרת המרכיבים שלה יהיו שונים .

כל הודעה מכילה מפתח שמצביע על סוג ההודעה הבאה על פיו אני יודעים לדלות את המידע של הheaders המתאימים.

5

מבחינת פיזור המידע ב NBL  המידע ערוך בצורה הבאה :

 

6

Object diagram

7

מציאת הFirst Network buffer מתבצעת בצורה הבאה

8

להודעת udp יש שני Buffers הראשון של הheader והשני של המידע .מציאת הBuffer  ים של המידע מתבצעת בשיטה הבאה :

9

בקוד הבא ניתן לראות את הדרך בא מוצאים את ה packet הראשון של ה Header .

ndis הינו little indian כלומר צריך להפוך את מיקום ה Byts בStructs כאשר מלבישם עליו מערך של bytes  

13

משתמשים בפונקציה RtlUshortByteSwap על מנת לבצע את ההצרחה של ה Bytes.

הקוד שמוצא את הpattern ומחליף ערך הינו :

14

אנו מוצאים את הpattern באמצעות NdisEqualMemory ומחליפים char אחד בו .

בUdp אין צורך לעדכן Check sum

כלי עזר מאוד חושב הינו ה Wire Shark , יש לשים לב שwire shark איננו עובד במקביל ל Kernel debuging

הרבה מאוד פעמים structs של אוביקטי רשת שמורידם מה internet אינם מדויקים ובאמצעות wire shark ניתן לבדוק ולהשוות לשמה בdriver .

את הקוד ניתן למצא כאן

 

Implements bridge pattern with wcf

באחד מהפרויקטים שעבדתי עליהם לאחרונה היתה דרישה להחליף תוכנת עמדה  במערכת מבזורת בתוכנה אחרת .

התקשורת בין כל העמדות הינה WCF  ,כאשר הcontract  של העמדה החדשה שונה לחלוטין מהcontract  הקיים ואין אפשרות להחליפו לזה שהיה קיים .(העמדה הייתה ונשארה server )

ל Contract החדש אין תאימות חד חד ערכית ברמת הקריאות כלומר חלק מהקריאות של ה Client  היו צריכות מספר קריאות בContract של ה Server החדש חלק היינו צריכים לשמור ב Cache  על מנת לאגד מספר קריאות מה client לקריאה אחת בServer וכד.

1

 

חשבנו על 3 פתרונות אפשריים

פתרון 1 מערכת חיצונית

לכתוב או לקנות מערכת חיצונית שהינה מאין biztalk sever  אשר יבצע את פעולת התרגום .2

 

 

 

יתרונות

מבחינת decoupling מאוד גבוה בין מנגנון ההמרה לאפליקציה , השינוי היחיד שנדרש לבצעו באפליקציה זה החלפת ה address  של ה wcf server .

אפשרות ניטור של המידע הזורם + העבודה המתבצעת באותו מתאם .

תמיכה יכולת גידול לעתיד לdevice ים או שיטות תקשורת שונות .

חסרונות

הבעיה שצריך לתחזק עוד אפליקציה \ service  שאיננו חלק אינטגרלי מהאפליקציה .(תיעוד התקנה קיטלוג )

לקנות אפליקציה חיצונית עולה כסף .

Hop  נוסף של מידע שאין בו צורך . המידע נשלח ל wcf service  ולאחריו ל עמדה הנוספת. 

 

פתרון 2 השיטה הסינית

שיטת הquick and drity  לאתר את כל המקומות בקוד שפונים לwcf client  ולשתול בכל המקומות switch ים של האם לשלוח בintrerface החדש או הישן . + להוסיף את כל הלוגיקה בתוכנה עצמה של ה Client .

יתרונות

למרות שיש הרבה מאוד עבודה העבודה פשוטה אין סיכון טכנולוגי .

חסרונות

מכיוון שבאפליקציה  אין שיכבה אחת מסודרת שקראה לwcf clients  אלא הקריאות התבצעו מכל מקום אפשרי וישנם מאות מקומות כאילו הקוד של האפליקציה יסרבל  עוד יותר כי זה לא רק הקריאה עצמה יש להוסיף לוגיקה לכל קריאה .

כמובן שאפשר לעשות את זה יותר אלגנטי ולא להשתמש בSwitch אלא שימוש ב Factory   שיחזיר Iterface  ל ממשק .

 

הפתרון ה 3 Wcf custom channel

פתרון באמצעות Custom protocol channel שכתבנו שתפקידו לתרגם את ההודעות .

יתרונות

פתרון שהינו מאוד decoupled  אין צורך לפתוח אף אחד מהתוכנות . אפשר באמצעות קונפיגורציה להכיל אותו .

 לא נוצר עוד hop  מיותר

מאתגר מקצועית

אפשר באמצעותו ללמוד wcf  לעומק

מאוד מרשים ראשי צוותים סמי מיקצועיים (כאילו שיועדים מה זה wcf   בשתי מילים אבל אין להם שמץ ניסיון בארכיטקטורה נכונה וכד )שאתה מספר להם ברעיון עבודה שעשית כזה דבר חושבים איזה ילד פלא אתה ,השועלים הותיקים לא יתנו לך לסיים את הטרילוגיה והם כבר יראו לך את הדלת. שתסבך עד מוות את הפרויקט של מישהו אחר  .

חסרונות

מאוד technology aware  כלומר מבוסס על יכולות ההרחבה של wcf  ותלוי ביכולות האלו . ומכיוון ש wcf איינו קוד פתוח ניתן להיתקע עם memory leak בעיית ביצועים או חלקים שבלתי ניתנים למימוש .

 

Wcf channels

התשתית של wcf  מכילה ערימה של channels  המתפקדים כ chain of responsibility

דומה מאוד ל ndis filter drivers  .

3

 

 

בין כל החלקים של ה channel  זורמים אוביקטים מסוג Message .

הMessage

מורכב מ Headers  וPayload  של המידע (הAction  והפרמטרים שלה ).

כל channel  יכול להוריד להוסיף או לשנות את ה header ים .

כמו כן ה Channel יכול לשנות את ה payload כלומר את המידע עצמו שעובר ב Message

(שינוי פשוט של מידע יכול להתבצע באמצעות Behaviors כדוגמת מימוש IOperationBehavior ביתר קלות )

 

הפתרון שלנו התבסס על זה ש Channel במקרים מסוימים יכול להחליט שהוא לא מעביר הלאה הודעה (כלומר חוסם אותה ) או מעביר כמה הודעות במקומה )  או מעביר כמה הודעות בזמנו החופשי בthread  אחר אחרי שההודעה בכלל נסתיימה .

 

הפתרון הזה עובד טוב רק ב interface אסינכרוני .בinterface סנכרוני המצב מסתבך כי המערכת מנסה להחזיר לך תמיד הודעה על כל קריאה באופן סינכרוני ויצירת מצב ששתי הודעות נשלחות ומקבלים רק תשובה אחת מאוד בעיתי , אינני יודע אם אפשרי בכלל.

 אנו כתבנו Custom channel משלנו והכנסנו אותו ל channel  stack  .

15

ה  Channel הכיל את הלוגיקה כלומר את המימוש של ה Bridge  שמתאם בין ההודעות , הchannel  הינו statefull  הוא זכר את כל המידע מהקריאות הקודמות שדרושות לצורך התאום .  

הפיתרון מכיל את ה class ים הבאים :

7

LogicOrcBinding

הcustom binding  שמכיל את ה binding elements  שלנו .

 

4

 

הגדרנו רק 2 elements  כאשר תשתיות ה wcf  מוסיפות את החסר  בהתאם לקונפיגורציה אם זה security  , relaibale session  וכד

התשתית מוסיפה את שאר ה elements במקומות הנכונים כך שהצפנה לדוגמה תתבצע אחרי הטיפול שלנו .

 

 
5
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 

LogicOrcBindingElement , LogicOrcChannelFactory , LogicOrcChannelListener

מכילים את ה בוילרפארט קוד בד"כ לא נוגעים או משנים אותם. תפקידם לעטוף את היצירה של ה  channel שלנו .

כאשר יש פרמטרים שצריך להעביר מה Binding  עצמו ,צריך לדאוג להעביר דרכם .

 

   

 

LogicOrcBodyWriter

class עזר פשוט שתפקדו לעזור בלכתוב את ה פרמטרים של הAction בMessage

הClass  מכיל event  בשם  writeBodyCallback שנקרא אוטומטית על ידי המערכת לצורך עדכון הפרמטרים .

 

6

 

LogicOrcChannel

זהו לב המערכת

8

 

בCtor אנו מקבלים Reference ל Channel הבא בstack

 9

אנו נשתמש בReference הזה לשני דברים :

1.להפנות מימוש methods למימוש ב Channel הבאה בstack ב methods שאין לנו צורך לממשן

לדוגמא:

10

2.הפניית הMessage אחרי הטיפול לChannel  הבאה ב stack

 

הmethods  שמטפלות בשליחת הנתונים ל channel הבאה בstack הינן :

public void Send(Message message, TimeSpan timeout) ,

public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)

כאן משתלבת הלוגיקה העסקית של הBridge בשליחה של הודעות לServer ,ניתן לא להעביר את הMessage ל channel הבא ובכך לחסום את ההודעה ניתן להעביר הודעה אחרת או מספר הודעות אחרות ,כמו כן ניתן להעביר את הטיפול ל thread אחר ובזמן שונה להעביר הודעה ל Channel הבא ב Stack .

 

11

 

דוגמא למימוש

פונקצית עזר לבניית Message חדש בהסתמך על העתקת ה Header של ה message  שהתקבל מבChannel הקודם ושינוי ה Action

12

 

הפונקציה הבאה מקבלת פקודה אחת ומפצלת אותה ל שניים

 

13

 

הMethods שמטפלות בקריאת הנתונים בצורה סינכרונית או אסינכרונית בהתאמה הינן :

public bool TryReceive(TimeSpan timeout, out Message message)

והצמד

public

IAsyncResult BeginTryReceive(TimeSpan timeout, AsyncCallback callback, object state)

public bool EndTryReceive(IAsyncResult result, out Message message)

 

אחרי ששלחנו שתי הודעות Call1 וCall2 ל Client אמו מקבלים גם שתי תשובות אותן אנו צריכים לאחד לתשובה אחת .את הפעולה הזו אנו עושים ב EndTryReceive

14

 

Exception

בזמן הפיתוח אם מבצעים שגיאה בקריאה של ה Contract לדוגמא קוראים ל Action שאיננו נמצא ב contrat של ה Server המערכת נותנת שגיאה מפורטת :

{"The message with Action 'http://tempuri.org/ILogicOrcSample/CallWithWrongAction' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver.  Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None)."}

טעיות בשמות של ה פרמטרים בד"כ יגרמו ל להעברת null בפרמטרים של ה method

 

בדיקות

שתי בדיקות נעשו לצורך ולידציה של הפתרון :

1.בדיקת memory leak שהמערכת לא משאירה אצלה זבל במקרה ונחסמת פקודה מלהגיע ליעדה וכד.על מנת לבצע את הבדיקות עם התוכנית דוגמא יש להוריד את כל ה חלק של ה  Diagnostic מה Configuration files , לבצע את הקריאות הנדרשות בloop ומידי פעם לקרא ל GC.Collect

2.בדיקת ביצועים ש ה Bridge Channel לא פוגע בביצועים כתוצאה מביצוע הלוגיקה שלו

בבדיקה שבצעתי לא נראה כל דליפת זיכרון אך אני ממליץ בחום לכל מי שחושב על הפתרון לבדוק גם את נושא הזיכרון , יציבות , ביצועים של הקונספט האם הם מתאימים לדרישותיו.

קישור לקוד דוגמא:

קוד דוגמא מאוד בסיסי נמצא כאן .

הקוד מבוסס על דוגמא של מיקרוסופט בשם Chunking channel לדוגמא הוכנסו רק הדברים האלמנטריים ביותר לצורך הדגמת הקונספט .

על מנת להשתמש בה יש כמוסן להוסיף לה את כל הקוד הסטנדרטי כגון טיפול ב Timeouts סגירת Channels  ללא exceptions  וכד.