วันอังคารที่ 27 พฤษภาคม พ.ศ. 2557

ดัก ErrorMsg ป้องกัน Dump กัน

ดักจับ ErrorMsg กัน

บางครั้ง โปรแกรมเมอร์  มักพบปัญหาที่คาดไม่ถึง "ชนิดยาก"
เช่น Error เกิดขึ้นตอนกลางคืน (ขณะทดสอบอย่างหนัก ไม่ยักพบปัญหา) เป็นต้น

การดักจับ Error (handler) เทียบกับภาษาอื่น
- ใน CL  คือ  MonMsg
- ใน .net  คือ Try .. Catch ...

การดักจับ Error ใน RPG มีได้ 4 วิธี


  • ใช้ Error indicator หรือ an 'E' operation code extender 
  • ใช้ MONITOR group
  • ใช้ Error subroutine 
  • ใช้ Default exception



แต่ละวิธี  มีจุดเด่น/จุดด้อย ต่างกัน  คนที่สนใจไปอ่านเพิ่มได้ครับ
วันนี้เราจะมาพูดถึงตัวที่   ทำง่าย ที่สุด นั่นคือ  Error Subroutine ครับ

เพียงแค่ประกาศ  เพิ่มส่วนนี้
CL0N01N02N03Factor1+++OpcdeFactor2+++ResultLenDHHiLoEq
C*=================================
C           *PSSR     BEGSR                        
C*-------------------------
C**
C                     SETON                     LR 
C   LR                RETRN                        
C                     ENDSR                        
C*=================================

ประยุกต์ อื่นๆ
- ให้ทำงานต่อ หรือ หยุด  หรือ ทำ Rollback (อันนี้ผมไม่ได้ใช้)
- ให้ ส่งค่าตัวแปร  กลุ่มที่อยากรู้ออกมา เช่น ใช้คำสั่ง  DSPLY
- ให้ ส่งสถานะ  ตำแหน่งที่มีปัญหา,ชื่อ Error   (ตัวแปรจากระบบ)
IDsname....NODsExt-file++.............OccrLen+..........
I           SDS                                     
I                                     *ROUTINE ELOC 
I                                     *STATUS  ESTS
I                                        1  10 PPGMID
I                                       11  150PSTS  
I                                       21  28 PLINE  

Note ถ้าใช้ RPGILE จะมี ตัวช่วย ให้เลือกมากกว่านี้

การทดลอง
รู้แล้ว ควรจะทดลองซะหน่อย ครับ

1.  เขียนโปรแกรมสั้นๆ      1 หารด้วย 0   

C           100       DIV  0         J#      30   
C                     SETON                     LR
C   LR                RETRN                       

     สั่ง run  ... จะเกิด Error ดังนี้
xxxx 900 tried to divide by zero (factor 2) (C G S D F).

    ให้ตอบ Msg เป็น "D" = dump  เราจะได้ Spool  และเห็น  ค่าตัวแปรต่างๆ ณ. จุดที่เกิด Error

RPG/400 FORMATTED DUMP                                         
Program Status Area:                                           
Program Name . . . . . . . . . . . . . :   QTEMP/xxxx        
Program Status . . . . . . . . . . . . :   00102               
                tried to divide by zero (factor 2) (C G S D F).
Previous Status  . . . . . . . . . . . :   00000               
Statement in Error . . . . . . . . . . :   900                 
RPG Routine  . . . . . . . . . . . . . :   *DETC               
Number of Parameters . . . . . . . . . :   001  

...

*...+....1....+....2....+....3....+....4....+....5....+....6....+
 ZIGNDECD 0603C4  CHAR(1)       '0'                             
 ZPGMSTUS 05FDF6  CHAR(400)     'XXXXXX    0010200000900     
          05FE55    +96         '                            
          05FEB4    +191        '        20                  
          05FF13    +286        '380528141106300001QRPGSRC   

    Note ตำแหน่ง ค่าต่างๆในตัวแปร ดูได้ที่ ZPGMSTUS
    Note ค่าใน Spool หลายตัว ส่งผ่านเป็น  ตัวแปร  นำมาใช้งานได้  ESTS กับ PSTS เป็นค่าเดียวกัน


วันอาทิตย์ที่ 9 กุมภาพันธ์ พ.ศ. 2557

5 ทักษะที่ ควรมี

ผมได้ไปอ่านบทความ  ที่  http://search400.techtarget.com
คือ 9 ทักษะที่ควรมี  (ในสายงาน RPG)
แต่ผมปรับเนื้อหาให้เหมาะกับ  สภาพแวดล้อมที่ผมเห็น การเปลี่ยนแปลงในองค์กรมาให้ดู

RPG โปรแกรมเมอร์ที่ทำงานมานาน  จะคุ้นเคย (และรู้สีกดี) กับสภาพแวดล้อมที่ปิด(จำกัด)
(จำน้อยๆ  ทำอะไรมากไม่ได้   กลายเป็นไม่จำเป็นต้องทำอะไรมาก ด้วย)

แต่ในความเป็นจริง  Application ในภาษาอื่นๆ มีจุดเด่นมากขึ้น
และ User ก็ได้เห็น (เปิดโลกไปแล้ว)  ทำให้เราหลีกหนีไม่ได้

สำหรับ  สายงาน RPG ผมแนะนำให้รู้  Skill ดังต่อไปนี้ครับ (ช่วงเริ่มต้น)
ซึ่งจะทำงาน  ของคุณ  "ง่ายขึ้น" (เรียนรู้ + ฝึก   แล้ว นำมาใช้ได้เลย)  ดังต่อไปนี้
1. SQL
2. DB2/400
3. RPG IV 
4. ใช้ Tool : Code/400
5. ใช้ Tool : iSeries Navigator 


1. SQL 
    พนักงานรุ่นใหม่ ถูกสอนให้ใช้  SQL ในการติดต่อกับฐานข้อมูล
    ดังนั้น  เพื่อให้คุยกับเขารู้เรื่อง  ก็จำเป็นต้องใช้  เหมื่อนกันครับ  
             ควรรู้สิ่งดีๆ เหมือนคนรุ่นใหม่  มากกว่าให้คนรุ่นใหม่ มาหัดใช้วิธีเก่า/ช้ากว่า

    ควรใช้  Run SQL Script  ใน   iSeries Navigator 
    - จัดเก็บ แยก SQL ที่ใช้บ่อยได้   เช่น SQL ที่ใช้หา  ความผิดปรกติของ data

2. DB2/400    หลายคนแค่เรียนรู้ว่า  ใช้อย่างไร ? (และจำกัด)
    แต่ในความเป็นจริง DB2 มีหลักการดีๆ  ที่เกิดขึ้นก่อนเจ้าอื่นๆ  
    เพียงแต่ใน RPG ยุคแรกๆ ไม่ได้ใช้งาน
    >> ควรอ่าน เทียบกับ Std Database (พร้อมจะอ้างอิง, ใช้ กับ Database อื่นได้)




3. RPG IV   
    เริ่มเขียนในสไตล์นี้   ibm พยายามให้ทีมงานเดิมสามารถปรับเปลี่ยนได้  โดยการเปลี่ยนทีละน้อย  ข้อดีก็คือ  จะรู้สึกไม่ยาก (เมื่อเทียบกับไปเรียนภาษาใหม่)

    -Sub procedures    
     ใน RPG-II,III  เรามักจะเขียนต่อเนื่องกันไป (Top-Down) และใช้ Goto  เป็นหลัก
     ทำให้อ่าน code ยาก  ตั้งแต่ RPG IV แทบทุกอย่างจะเขียนแบบ Sub-Procedure

    -Integrated Language Environment (ILE)
     RPGILE เป็น  อีกระดับของ RPG ที่  "เปิด" กว้างในการติดต่อกันระหว่าง Application
     ทำให้เราข้ามขีดจำกัดของแต่ละภาษา   เช่น  ติดต่อกับโปรแกรม Java ได้

4. ใช้ Tool : Code/400   
    เป็นโปรแกรมที่อยู่ใน WDSC WebSphere Studio แล้ว  (ติดตั้งจาก CD ที่ได้มา   ติดตั้งบน Windows)
    ทำให้การเขียนโปรแกรม  ได้เร็วและง่ายขึ้น  (มีคนสรุปว่า เร็วขึ้นมากกว่า 20%)
    คิดเล่นๆ  - นำ code กลับไปทำที่บ้าน หรือ offline ได้  (compile ไม่ได้)
                   ช่วยในการ  ร่างโปรแกรม (draft)

5. ใช้ Tool : iSeries Navigator หรือ System i Navigator
    เป็นเครื่องมือพื้นฐานที่ติดตั้งมาบน CA/400 (Client Access/400) 
    -Operations Navigator       เดิม   เราต้อง "จำคำสั่่ง"  ในการจัดการต่าง  แต่ด้วยตัวนี้มองผ่านภาพ,กลุ่มคำ  แล้วเลือก  (ในสไตล์ windows)  ทำให้ใช้งานได้ง่ายกว่ามากครับ   เหมาะกับคนที่จำคำสั่งได้เล็กน้อย
    -Databases function          ต่อเนื่องจาก ข้อ 1  ครับ


ในบทความพูดถึง  การมีทักษะกับ  The Integrated File System (IFS)
แต่ผู้เขียน  ไม่ได้ทำงานกับงานลักษณะนี้  จึงขอบายครับ

Tips เพิ่มความเร็วใน Code-1

คำเตือน - Tips บางเรื่อง  เหมาะกับบางเหตุการณ์ น๊ะครับ
ผมพยายามจะ  บอกว่า  มัน "น่าทำ" ใน เหตุการณ์ไหน ? 
ดังนั้น  อย่าท่อง  "แบบย่อ"    ถ้าใช้ผิดเรื่อง อาจทำให้  งาน "ช้าลง" ได้ครับ

A. ลด Condition
     ที่มา   การทำงานแบบ Cond จะต้องใช้  "CPU มาก" (คนอ่าน Code ก็อ่านยาก)
               การเขียน  RPG ทั่วไปจะเขียนสั้น  ไม่ใช่ Structure  ทำให้ต้องแยกเป็นชิ้น

 *.. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+.
C           W1SEL      COMP 'A'                      31 ADD
C           W1SEL      COMP 'C'                      32 CHG
C           W1SEL      COMP 'D'                      33 DEL
 *...
C           KG122      CHAINPG010L2              80      
C    31     *IN80      CABEQ'0'                      41 ERR 
C   N31     *IN80      CABEQ'1'                      42 ERR

     ตย. ข้างต้น   ในทุกรอบ จะต้องผ่าน 5 Condition

     เมื่อเปลี่ยนมาแบบนี้  จะทำงานเร็วกว่า (ผ่าน 2 - 4 Condition)
 *.. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+.
C           KG122      CHAINPG010L2              80      
C                      SELEC
C           W1SEL      WHEQ 'A'                      
C           *IN80      CABEQ'0'                      41 ERR 
C           W1SEL      WHEQ 'C'                      
C           *IN80      CABEQ'1'                      42 ERR
C           W1SEL      WHEQ 'D'                      
C           *IN80      CABEQ'1'                      42 ERR
C                      ENDSL

B. อ่านได้เร็ว และ เข้าใจง่ายขึ้น
    แทนที่  การกำหนดค่ายาวๆ  ด้วย  Constance  

    ในภาษาใหม่  เรามักกำหนด  constance ไว้ด้านบน  (ทุกคนจะเห็นผ่านตา  ก่อนทำงาน)
    แต่ใน RPG   การกำหนดค่าที่ยาวๆ    มักจะใช้ Array ซึ่งต้องประกาศไว้  "ท้าย" โปรแกรม

    โดย  กำหนดตัวแปร  Constance ใน I-Spec   (ต้องประกาศไว้ด้านบน)
    ตย. ต้องการ  ตรวจว่า  มีอักษรไหน ไม่ใช่ ตัวเลข  ใน String หรือไม่ ? เริ่มตรวจที่ตำแหน่งที่ 3
... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6
I*    field Data for CHECK                             
I            DS
I              '0123456789'          C         DIGITS  
... 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6
C*                    -----CHECK *all is DIGITS        
C                     MOVE 'xx123z5' STRING 7         
C           DIGITS    CHECKSTRING:3  RESULT 30      80
C*                ==> result = 6  found *in80='1'         



วันเสาร์ที่ 8 กุมภาพันธ์ พ.ศ. 2557

จะทำ Security กับ File อย่างไร ?

จะทำ Security กับ File อย่างไร ?

ในระบบที่สร้างมานาน   กับบริษัทฯ ที่ไม่กังวลเรื่องความปลอดภัย
เช่น  โรงงาน ,บริษัท(ที่ไม่ใช่  ธนาคาร,ประกันชีวิต)

การออกแบบ  กับ Security   มักจะออกแบบให้  ง่าย  ไว้ก่อน
เนื่องจาก Security จะทำให้ดี ต้องรู้จริง  และ วางแผน (ทั้ง  องค์กร, วิธีทำงาน และ IT)
ในอดีต  ไม่มีใครสนใจ  และไม่มีใครตรวจ

การทำ Security อาจจะอยู่ในรูปแบบ
-  Lock ด้วย IT  ที่ประตูทางเข้า         UserProfile
- ห้าม!  ด้วย  วาจา, ประกาศเป็นกฏ   (ต่างๆ)
- (ยังเชื่อใน)  ความไว้วางใจ  ว่าข้างต้น  ยังถูกต้อง

วันนี้  สภาพแวดล้อม  ที่เปลี่ยนไป
- มีกฏหมาย, มีหน่วยงานตรวจสอบ ทั้งจากภายในและภายนอก
- ต้อง Share ข้อมูลต่างๆ มากขึ้น
- ผู้ใช้งาน  มีมากและหลากหลาย

ผลที่ตามมา
- ไม่รัดกุม (ทางเข้าในปัจจุบัน  มีมากกว่าที่เห็น)
- ขาดความยืดหยุ่น - มีข้อมูลแต่  ห้ามๆๆๆ  จน Share กับใครไม่ได้ หรือ ขั้นตอนมาก
- เมื่อมีคนมากขึ้น   ความเชื่อใจเปลี่ยนเป็น  ไม่ไว้ใจซึ่งกันและกัน

การออกแบบ  ที่ดี  คือ  ทำตามคำคู่มือในหนังสือของ iSeries
ในขณะที่ คำแนะนำ  "เหมาะสม"   จะเกิดจาก  การปรับสภาพแวดล้อม
- องค์กร, หน้าที่ ที่ชัดเจน
  สิ่งที่จะเข้าใช้  (Function, Object)  ...  กำหนดหน้าที่ + สิทธิ  ในการ  เข้าใช้
- วิธีทำงานปัจจุบัน  ... ทบทวน ปรับวิธีจากความคุ้นเคย  ให้เหมาะ  กับ  หน้าที่+สิทธิข้างต้น
- IT ... จัด  เทคนิค,ตัวเลือก ที่เหมาะกับสิ่งที่ต้องทำข้างต้น

ตย.
* ลงทะเบียน ระดับบุคคล + จัดสิทธิร่วม  หลาย บุคคล  ให้เป็น  "กลุ่ม"
  (IBM แนะนำให้เริ่มที่  UserProfile)

- บางบริษัทฯ  มี admin  คนเดียวแต่มี programmer หลายคน
  เพื่อลดงาน Admin  ได้ให้ programmer สร้าง database จัดการกันเอง
     >> OS จะมองไม่เห็น  "ตัวตน"  การจัดสิทธิใน Lib หรือ File จะทำ "ยุ่งยาก"
     >> เมื่ออ่านต่อจนจบ  จะพบว่า  การเลือกวิธีนี้  ทำให้  งานด้านหลังทำ "ยากขึ้นไปอีก"

# ถ้าอนุญาต ให้ Programmer จัดการ (และมักจะให้ User Admin ทำต่อ)
   >> Admin เพียงแค่ สร้างโปรแกรมแล้วให้  Programmer หรือ User ระดับ admin จัดการได้
         ปัญหาอยู่ที่ไหน ? Admin ไม่เขียนโปรแกรม, ใช้เครื่องมือใหม่ ไม่คล่อง, ...
         แก้ไขให้ถูกจุด -> งานจะวิ่งต่อได้  สะดวก

* การออกแบบ P-file และ L-File  ที่มีโครงสร้าง  เหมือนกัน (จำง่าย)
   เช่น    P-file    ข้อมูลพนักงาน  มี  เงินเดือน
             L-file    แบบเดิม จะเห็นทุกช่องเหมือน P-file
             --- ไม่อนุญาตให้  ใครใช้ข้อมูลพนักงาน ---

     >> copy data ไป อีก table  (ตัด field ที่ไม่ต้องการ) ทำทุกคืน
             เกิดปัญหา  ไม่ real time   (ยื่นลา ตอนเช้า  ต้อง  รอดูผลพรุ่งนี้)
             Run โปรแกรมให้ ถี่ขึ้น  (AddJobSchE)
             หรือ  ใช้เครื่องมือของ Admin (เช่น Replicate)

   L-file มีคุณสมบัติ  อีกอันหนึ่ง คือ การสร้าง View ให้เหมาะกับ  การใช้งาน
     >> จำกัดสิทธิ การใช้  Lib, P-file
     >> สร้าง Lib  ที่แยก และ share แบบจำกัด
              สร้าง L-file ให้เหมาะกับ  การใช้งาน เช่น ไม่มี Field เงินเดือน

* ภาษา โปรแกรมที่ใช้  
   ภาษามาตรฐานใหม่  ที่ไม่ใช่  RPG   มีทางเลือกที่ยืดหยุ่นกว่ามาก
              ขนาดหน้าจอ, รายงาน  ที่เกินข้อกำหนด   จัดวางให้ใช้งานได้ง่าย
       ? ทีมงานดั้งเดิม ไม่ยอมเปลี่ยน
       >> แผน/ขั้นตอน การเปลี่ยน ภาษาหลัก  หรือ  เพิ่มภาษารอง  ในการใช้งาน
       >> ถ้าทำได้  จะมีทางเลือก ต่อไปนี้ช่วย

   สร้างเป็น Class  ที่ Encapsulate แล้ว  (ไม่เห็น Source Code) ให้เรียกใช้
           (เหมาะกับ  ภาษารอง ใช้ภาษาเดียว  เช่น  Java, ASP, Php  เป็นต้น)
   Web Service : วิธีการที่ถูกออกแบบมาเพื่องานลักษณะนี้  อาจจะมีข้อจำกัดมากกว่า เพราะทำบน internet ได้
          >> สร้าง Web Service ด้วย Java, DotNet

ข้อแนะนำอื่นๆ

- ทดสอบ  ก่อนเปิดใช้
       การปรับแล้วทำเลย  สไตล์ลูกทุ่ง   ไม่เหมาะกับงาน Security บางกรณีต้องหยุดเครื่องกันเลย
- จะเห็นว่า  ปัญหาใหญ่  คือ  ทีมงานหลัก
       Key Man ถ้ามองแค่  "เบา" สำหรับตนเอง   จะกระทบกับภาพรวม
       คนที่เกี่ยวข้อง  ถ้ายังคิดว่า   อะไรก็ได้  แต่ทำแบบเดิมๆ  น๊ะ  (ก็ไม่ได้ทำเพื่อเป้าหมาย)

วันพุธที่ 22 มกราคม พ.ศ. 2557

DB2 กับ RPG ภาค2

ผู้ที่ระบบที่ใช้ต่อเนื่องมานาน เกิน 15 ปี
น่าจะพบปัญหาเดียวกันนี้น๊ะครับ

Q1: Physical File = สร้างโดยไม่ระบุ Primary Key (Unique)

    แล้วจะรู้ได้อย่างไรว่า  Primary Key คือ อะไร ?
    การ Insert/Update/Delete จะชี้ได้ถูกต้อง อย่างไร ?

A1: ถ้าระบบฯ  มีอยู่แล้ว
    - เชื่อว่า โปรแกรม Maintenance ดั้งเดิม  ทำงานถูก  ได้ช่วยไกด์เรา
    - ตรวจสอบซ้ำ (จากตัว data)

SQL :  Select   Primary Keys  ,count(*) From Lib.File    
      Group by Primary Keys     
      Having   count(*) > 1

ถ้ามี  rows ออกมา  แสดงว่า  Primary Keys ไม่ถูกต้อง

อธิบาย
- Primary Keys (มีมากกว่า 1 Field ได้)
- ตามนิยาม เมื่อชี้ข้อมูลด้วย  Primary Keys จะพบแค่ 1 row เท่านั้น
- หลังการ  กรองค่า (Having)  ถ้า Primary Keys ถูกต้อง  ต้องไม่มี rows แสดงออกมา

อืนๆ - บางที่  จะมีการเก็บ ข้อมูลที่ "ลบ" ไว้ใน File  (ทำให้  ข้อมูลกลุ่มนี้  ไม่เป็นไปตามกฏ)
ดังนั้น ต้องแยกข้อมูลกลุ่มนี้  ออกไปก่อน  
เช่น itemNo ถ้าลบ จะใส่  "#" นำหน้า  ป้อนแล้วลบ  2 ครั้ง  จะมีรายการนี้ซ้ำ 2 รายการ

    - ตรวจจาก Business Concept
      เช่น   Stock File  (ItemNo, ItemName, WH , Loca, Vendor, OnHandQty)
               นิยามว่า   ItemNo  จัดวางอยู่ที่  พื้นที่ (WH,Loca)
               Primary Keys = ItemNo, WH(WareHouse),Loca

               นิยามว่า   ItemNo  จัดวางอยู่ที่  พื้นที่ (WH,Loca)  โดยต้องแสดงแยกตาม Vendor
               Primary Keys = ItemNo, WH(WareHouse),Loca, Vendor

               >> จะเห็นว่า  ถ้าใครโชคดี  ใช้ DB ที่มีโครงสร้างสมบุรณ์  (มี Field มาก)
แต่ไม่ได้กำหนด Primary Key ไว้   สามารถเดา  "ได้หลากหลาย"

Q2: ทำไม สร้าง Field ใน Logical File เหมือนใน Physical File

       สร้างแตกต่าง  ระดับ Field ได้หรือไม่ ?
A2: ได้ครับ
       ดั้งเดิม  เราจะทำให้มัน  เข้าใจ(จำ)ง่าย - เป็นเทคนิคที่ดีสำหรับระบบขนาดใหญ่
       แต่ระบบ DB ปัจจุบัน  ที่สอนกัน (จนเป็นมาตรฐาน)
       แยกเรียก Logical File  ว่า Index กับ View    
       - View   ว่าสร้างเพื่อให้  คนอื่น  ใช้งานง่าย หรือ จำกัด Security 
       - Index   ทำเพื่อความเร็วในการ Access 


CL - สร้างโปรแกรมช่วย ลดการทำงาน-1

CL - สร้างโปรแกรมช่วย ลดการทำงาน-1

CL = Control Language เป็นภาษาที่ใช้จัดการ  คำสั่ง  บน OS/400
ถ้าเทียบกับ Windows  ก็คือ เขียน/สร้าง Batch File เพื่อใช้คำสั่งย่อย ต่อเนื่องกัน

เทคนิคช่วยลด เวลา  ในการทำงานซ้ำๆได้ครับ

ตย. ทุกเดือนต้อง Backup Data  (เช่น Lib1/File1 -> LibB/FileH) โดยกำหนดขั้นตอนดังนี้
(1). แยกข้อมูลตาม ช่วงเวลา (เช่น  1 member ต่อ 6 เดือน เป็นต้น)
(2). เมื่อย้ายข้อมูลเสร็จ  จะนำข้อมูล
    2.1 จัดเก็บใน  Cartridge
    2.2 จัดเก็บใน  History Server (ที่ราคาถูกกว่า เช่น MS SQL Server)
          ต้องรวม Member กลับไปเป็น 1 Member
          (LibB/File1-หลาย member -> LibH/FileH 1 member)

ผมสนใจข้อ 2.2 ซึ่งต้องทำแบบ Manual ดังนี้
a. ตรวจดูว่า LibB/File1  มี member ชื่ออะไรบ้าง ? ด้วยคำสั่ง
    DSPFD  FILE(LibB/File1)  TYPE(*MBRLIST)
    (จดรายชื่อไว้)
b. Copy รวม data ด้วยคำสั่ง
    CPYF  FROMFILE(LibB/File1)   TOFILE(LibH/FileH)   FROMMBR(ระบุชื่อ)  TOMBR(*FIRST)   MBROPT(*ADD)

ตย. มี 10  File แต่ละ File มี 5-10  member ที่ชื่ออ่านยาก    การทำแบบ Manual  จะใช้เวลา "นาน" และเสี่ยงที่จะผิดพลาด (จดชื่อผิด)

เราจะสร้างโปรแกรมช่วย ลดการทำงาน ในข้อ 2.2 กัน
โดยสร้างโปรแกรม และทำงานดังนี้   CALL   BK2HT   (LibB   File1     LibH  FileH)

สร้าง CL  โดยปรับขั้นตอนตามนี้
(รับค่า   &LibB  &File1  &LibH  &FileH)
a. DSPFD FILE(&LibB/&File1) TYPE(*MBRLIST)  OUTPUT(*OUTFILE)  OUTFILE(QTEMP/A)
Loop อ่าน File  QTEMP/A
       b. CPYF  FROMFILE(&LibB/&File1)   TOFILE(&LibH/&FileH)  
                       FROMMBR
(&mlname)    TOMBR(*FIRST)   MBROPT(*ADD)

จากนั้น  เพิ่มรายละเอียดต่างๆ  เช่น
- ตรวจ Lib/File ต้องมี
- ถ้ามี File แต่ไม่มี Member
- ...

มาดู code  BK2HT แบบเต็มๆกัน

---------------------------------------------------------------------
PGM        PARM(&LIBB &FILE1  &LIBH  &FILEH)                  
                                             
DCLF       FILE(A)                    
DCL        VAR(&LIBB)    TYPE(*CHAR) LEN(10)  
DCL        VAR(&FILE1)   TYPE(*CHAR) LEN(10) 
DCL        VAR(&LIBH)    TYPE(*CHAR) LEN(10)  
DCL        VAR(&FILEH)  TYPE(*CHAR) LEN(10) 

CHKOBJ OBJ(&LIBB/&FILE1) OBJTYPE(*FILE)
MONMSG     MSGID(CPF9801) EXEC(GOTO CMDLBL(STP090)

CHKOBJ OBJ(&LIBH/&FILEH) OBJTYPE(*FILE)
MONMSG     MSGID(CPF9801) EXEC(GOTO CMDLBL(STP090)

DSPFD FILE(&LibB/&File1) TYPE(*MBRLIST)  OUTPUT(*OUTFILE)  OUTFILE(QTEMP/A)

/*-- Start Loop --*/
STP010:
  RCVF                                               
  MONMSG     MSGID(CPF0864) EXEC(GOTO CMDLBL(STP090))
  CPYF  FROMFILE(&LibB/&File1)   TOFILE(&LibH/&FileH)  
        FROMMBR
(&mlname)    TOMBR(*FIRST)   MBROPT(*ADD)

GOTO       CMDLBL(STP010)

/*-- End Loop --*/
STP090:
ENDPGM
---------------------------------------------------------------------
ก่อน compile  ให้เรียกใช้คำสั่งข้อ  a.  ก่อน (เพื่อสร้าง Object = QTemp/A)

อื่นๆ

เทคนิคนี้ใช้บ่อย   ได้กับงานที่เรียกใช้ CL Cmd ติดต่อกัน
- ตรวจ data size ในระบบ file ไหนมีขนาดใหญ่ เกินกำหนด
- ตรวจ job ที่ (กำลัง) ทำงานใน subsystem
- ...

ตย. ข้างต้น  เมื่อดุดีๆ  จะพบว่า  ถ้าปรับขั้นตอน  (1)  ให้ไปเก็บใน LibH/FileH 1 member เลย  ก็ไม่ต้องทำ 2.2     ที่ไม่ทำเพราะ  (1) เป็นโปรแกรมที่เก่ามาก  และมีเทคนิคเฉพาะหลายอย่าง ... การปรับถือว่าไม่คุ้ม


วันอาทิตย์ที่ 11 สิงหาคม พ.ศ. 2556

การใช้ Array

การใช้ Array

หลักการ           

Array = มักใชักับชุดของตัวแปร    แทนที่จะประกาศตัวแปรที่มีลักษณะเหมือนกันหลายๆตัว เช่น  X1,X2,X3   โดยเพียงระบุ  X(1), X(2), X(3)   อ่านว่า X ลำดับ(element) ที่  1, X ลำดับที่ 2, X ลำดับที่ 3
Array ของ RPG/400 มีข้อจำกัดพอสมควรเมื่อเทียบกับภาษาสมัยใหม่อื่นๆ  เช่น  ทำได้แค่ 1 มิติเท่านั้น

ตัวอย่างที่ใช้งานบ่อยๆ
- เดือน 1 มีชื่อย่อว่า JAN      Mth(1) = JAN,  Mth(2) = FEB
- รวมค่าใช้จ่าย  แยกตามเดือน   Amt(1),Amt(2),…  เก็บค่าใช้จ่ายของเดือน 1,2, …
- รวมยอดขาย  แยกกลุ่มสินค้า   Gam(1),Gam(2),…  เก็บยอดขายกลุ่มสินค้า 1,2, …
- การค้นหา  อักษร ที่ต้องการ (วิธีใหม่ ใช้ Scan)
- การจัดการ String เช่น  ชื่อกลุ่มงาน ที่ 2 อักษร หลัง “-“ (MT-Z1001 -> Z1) , ตัดอักษรหลัง ตัวเลข  (608ZZ -> 608, R-1560HHMT -> R-1560)

รูปแบบการสร้าง ตัวแปร Array

I.  Array ว่างๆ  เป็นตัวแปรสำหรับใช้งาน
II. Array  ที่กำหนดค่าให้เลย   ด้วยตารางข้อมูลท้ายโปรแกรม
III. Array ที่อ่านจาก File โดยตรง 

I. การประกาศค่า Array ว่างๆ

รูปแบบที่ใช้กันบ่อย
ในการสร้างรายงานข้อมูลการขายสินค้าทั้งเดือน (เรียงตาม Invoice No) 
- แต่ต้องการให้ ให้ "สรุป" รวมค่าตามแต่ละรายการสินค้า  ท้ายรายงาน

ปรกติ ที่ท้ายโปรแกรม จะเพิ่มเปิด Logical-file ที่เรียงตามงาน   อ่านแล้วคำนวน
          หลายคนไม่ใช้วิธีนี้  เนื่องจาก  อาจจะพบบางกรณีที่รวมค่าแล้วไม่เท่ากับ  รายการด้านบน!  เพราะมีข้อจำกัดเฉพาะแทรกอยู่
ทางแก้ไข คือ ก่อนจะพิมพ์รายงาน (ด้านบน) ให้รวมค่าเก็บไว้ในตัวแปร 

ประกาศตัวแปร Array
E....FromfileTofile++Name++N/rN/tbLenPDSArrnamLenPDSComments+
E                    AR21       50 15   AQ22    7 2
ได้ AR21[...ขนาด 15 อักษร   จองพื้นที่ไว้ 50 ตัว 
     AQ22[…] ตัวเลข  7,2  (xx,xxx.xx  จองพื้นที่ไว้ 7 หลัก  2 หลักเป็นทศนิยม )  จองตัวแปรไว้ 50 ตัว

ลบค่าใน Array ทิ้ง  มักจะประกาศไว้ครั้งแรก (อยู่ก่อนเข้า Loop อ่านข้อมูล)
มักจะกำหนดตัวชี้ Element สุดท้ายใน Array  (เช่น  M2)
C....Factor1+++OpcdeFactor2+++ResultLenDHHiLoEqComments
C              CLEARAR21
C              CLEARAQ22
C              Z-ADD0         M2

ตรวจค่าใน Array ด้วย LookUp
ถ้าพบ  สะสมค่า, ถ้าไม่พบ ให้จัดเก็บค่าไว้ใน Array 

รูปแบบที่ใช้กันบ่อย
C....Factor1+++OpcdeFactor2+++ResultLenDHHiLoEqComments
C              Z-ADD1         I#      30
C    ItemNo    LOKUPAR21,I#                 80
C    *IN80     IFEQ ‘1’                       Found
C              ADD  qty       AQ22,M2
C              ELSE                           Not-Found
C              ADD  1         M2
C              MOVEItemNo     AR21,M2
C              Z-ADDqty       AQ22,M2
C              ENDIF
นำ ItemNo (ค่าจาก File)  ค้นหาใน Array  AR21   โดยเริ่มต้นลำดับที่ตามค่า I# (ควรเริ่มที่ 1)  
ถ้าพบ *Indicator จะ On (‘1’)   ตัวแปร I#  จะบอกลำดับที่พบ

ตย. ต้องการตัด  “IN”  ทิ้ง ใน  ITEM   เช่น  เป็น  “FISH IN OIL”  กลายเป็น “FISH  OIL”
ใช้ Array มาเก็บข้อมูล ด้านซ้าย และ ด้านขวา  โดยตรวจจาก ตำแหน่ง
ค่าเริ่มต้น                     ค่าสุดท้าย
1...5....0.       1...5....0
FISH IN OIL       FISH  OIL
                a.ค้นหาตำแหน่ง  "IN"  ใน ITEM 
                                a.1 พบเริ่มต้น  ตำแหน่งที่ 6     (จัดเก็บใน  I#)
                                a.2 "IN" มีความยาว 2 อักษร  ตำแหน่งถัดไป คือ 8  (6 + 2)  (จัดเก็บใน J#)
                b.นำอักษรถัดไป ถึงแม้ว่าจะเป็นช่องว่าง (X#5)  มาแทนที่  ตำแหน่งเริ่มต้นที่พบ IN

                เลือกใช้คำสั่งที่เหมาะสมได้  ดังนี้
E....FromfileTofile++Name++N/rN/tbLenPDSArrnamLenPDSComments+
E                    AR1        50  1
C*---  
               “IN“      SCAN ITEM,1   I#             80 a.
*IN80     IFEQ ‘1’                         Found
I#        ADD  2        J#                 a.2
          MOVEAITEM     AR,1
          MOVEAAR,J#    X#5
5         SUBSTITEM:J#  X#5               Or
          MOVEAX#5      AR,I#              b.
          END

ตย. อ่านข้อมูล   ได้ กลุ่มสินค้า เก็บใน  str    และ ยอดขาย  เก็บใน qty 
ต้องการรวม  ยอดขาย  ตาม  กลุ่มสินค้า การประกาศค่า โดย
                AR21                      ใช้เก็บ  กลุ่มสินค้า
                AQ22                      ใช้เก็บ  ยอดรวมจำนวน

E....FromfileTofile++Name++N/rN/tbLenPDSArrnamLenPDSComments+
E                    AR21       50 15   AQ22    7 2
C*…
C....Factor1+++OpcdeFactor2+++ResultLenDHHiLoEqComments
C              CLEARAR21
C              CLEARAQ22
C              Z-ADD0          M2
C*
C*---KEY1      SETLL file
C*---KEY1      READE file
C*---*IN80     DOWEQ '0'
C*
C              Z-ADD1         I#      30
C    str       LOKUPAR21,I#                 80
C    *IN80     IFEQ ‘1’                       Found
C              ADD  qty       AQ22,M2
C              ELSE
C              ADD  1         M2
C              MOVEstr        AR21,M2
C              Z-ADDqty       AQ22,M2
C              ENDIF
C*---
C*---          EXCPT DTL010
C*---KEY1      READE file
C*---          ENDDO
C*----
C*
C              XFOOTAQ22      T#QTY
C              DO   M2        I#
C              MOVELAR21,I#   O#ITNO
C              Z-ADDAQ22,I#   O#QTY
C              EXCPTTTL010
C              ENDDO

Xfoot                      รวมค่าใน Array ทั้งหมด (อยู่หลัง Loop อ่านข้อมูล)
รูปแบบที่ใช้กันบ่อย
C....Factor1+++OpcdeFactor2+++ResultLenDHHiLoEqComments
C              XFOOTAQ22      T#QTY
Tip          บางคนหลีกเลี่ยงคำสั่งดังกล่าวด้วยการเพิ่มคำสั่งลักษณะนี้ลงไป (เข้าใจง่ายแต่  อาจทำให้โปรแกรมทำงานหนักขึ้น)
C....Factor1+++OpcdeFactor2+++ResultLenDHHiLoEqComments
C              ADD  qty       T#QTY

Sort                        การเรียงลำดับใน Array
รูปแบบที่ใช้กันบ่อย
C....Factor1+++OpcdeFactor2+++ResultLenDHHiLoEqComments
C              SORT AR1    
ระวัง       หลังการเรียง  ข้อมูลกลุ่มที่เป็น Blank จะแสดงก่อน
                ทางแก้ไข   การนำไปใช้  ต้องเลือกรายการที่ไม่เป็น Blank
                Array ที่สร้างแบบเรียกใช้เป็นคู่ AR21, AQ22  การ SORT จะตัวเดียว จะทำให้ลำดับคู่ใน AQ22  ผิด
                ทางแก้ไข   รวม  AR21  กับ AQ22  ให้เป็น Array ตัวเดียวกัน  (ทำหลัง Loop อ่าน Data)  แล้วจึงเรียงลำดับ
C....Factor1+++OpcdeFactor2+++ResultLenDHHiLoEqComments
C              DO   M2        I#
C              MOVELAR21,I#   AR1              LEFT
C              MOVE AQ22,I#   AR1              RIGHT
C              ENDDO
C*
C              SORT AR1
C*
C              DO   M2        I#
C              MOVELAR1,I#    O#ITNO           LEFT
C              MOVE AR1,I#    O#QTY            RIGHT
C    O#ITNO    IFNE *BLANKS
C              EXCPTTTL010
C              ENDIF
C              ENDDO

ตย.  จากข้อมูลต่อไปนี้  (Logical File : Key = Order)
Order   Item   Qty
111          A      10
222          A      10
333          B      15
444          A      10
ใช้ Array  รวมค่าและแสดงผลรวม ท้ายรายงาน  ลักษณะนี้
# Total   =   45
         A       30
         B       15
E              AR21      50  15     AQ22   7 2
C*…
C              CLEARAR21
C              CLEARAQ22
C              Z-ADD0        M2         30
C    STP010    TAG
C*   -------------
C              EXFMTDSP01
C KA           GOTO STP020
C*
C              EXCPTHED010
C    KEY12     SETLLOrderL1
C              READ OrderL1                 80
C*   -------------------------------
C    *IN80     DOWEQ’0’
C*
C              Z-ADD1        I#         30
C    ITEM      LOKUPAR21,I#                 80
C    *IN80     IFEQ ‘1’                       Found
C              ADD  qty      AQ22,M2
C              ELSE
C              ADD  1        M2
C              MOVELITEM     AR21,M2
C              Z-ADD         AQ22,M2
C              ENDIF
C*
C OF           EXCPTHED010
C              EXCPTDTL010
C*
C              READ OrderL1                 80
C              ENDDO
C*   -------------------------------
C    STP020    TAG
C*   -------------
C              EXCPTHED010
C              XFOOTAQ22     G#QTY
C              EXCPTGRD010
C*
C              DO   M2       I#
C              MOVELAR21,I#  T#ITEM
C              Z-ADDAQ22,I#  T#QTY
C              EXCPTTTL010
C              ENDDO
C*
C              SETON                        LR
C LR           RETRN

II. Array  ที่กำหนดค่าให้เลย   จากตารางข้อมูลท้ายโปรแกรม

                ข้อสังเกต    กำหนดค่าใน E-Spec มี 3 ชุดตัวเลข  โดยเพิ่มจำนวน Data ใน 1 แถวข้อมูล
                ตารางข้อมูลท้ายโปรแกรม  ต้องวางท้ายโปรแกรมจริงๆ   โดยเริ่มต้นที่ด้วย  **  (Col.1,2)
                ถ้ามีมากกว่า 1 ตาราง  RPG จะเลือกแบบเลียงลำดับ
0010.00      E....FromfileTofile++Name++N/rN/tbLenPDSArrnamLenPDS
0011.00      E                    MT1     1  12  3
0012.00      E                    MT2     3  12  2
0013.00      C*…
FMT **   ...+... 1 ...+... 2
0076.00 ** For MT1 1 Line Contain 1 Data            
0077.00 JAN
0078.00 FEB
0080.00 MAR
0081.00 APR
0082.00 MAY
0083.00 JUN
0084.00 JUL
0085.00 AUG
0086.00 SEP
0087.00 OCT
0088.00 NOV
0089.00 DEC
0090.00 ** For MT2 1 Line contain 3 Data
0091.00 010203
0092.00 040506
0093.00 070809
0094.00 101112
สร้างโปรแกรมทดสอบ  
                รับค่า      เดือน (เลขที่)          W1MTH2
                Enter
                แสดงค่า  เดือน (อักษร)         W1MTH3

            MoveL*blanks  W1MTH3
            Z-add1        i#
W1MTH2      LokupMT2,i#             80
*in80       IFEQ ‘1’                    Found
            MoveLMT1,i#   W1MTH3
            ENDIF

III. Array  ที่กำหนดค่าให้เลย  จาก File

การกำหนดข้อมูลจาก File   ก็ต้องไปประกาศ  File ที่จะใช้  ดังนี้
             FFilenameIPEAF....RlenLK1AIOvKlocEDevice+......
0009.00      FFILE1   IT  F       3           EDISK
0010.00      E....FromfileTofile++Name++N/rN/tbLenPDSArrnamLenPDS
0011.00      E                    M1      1  12  3