วันอังคารที่ 26 สิงหาคม พ.ศ. 2557

Build Micro Python ไปใช้บน STM32F4

KickStarter โครงการแรกที่ผมจ่ายเงินซื้อผลิตภัณฑ์ แล้วมีความสุขมาก คือ MicroPython เป็นการพัฒนา Micro Processor board เล็กๆ น่ารักๆ ให้มีความสามารถของภาษา Python Embed ลงไปใน CPU ARM Cortex-M4 ซึ่งเห็นครั้งแรกผมไม่รอช้าที่จะ "เข้าคิว" เพื่อรอสั่งซื้อมาเป็นเจ้าของ เมื่อของพร้อม...

แล้ววันที่ของพร้อมก็มาถึง ผมสั่ง Micro Python ไปหนึ่งชุด พร้อม LCD Skin และ AMP Skin โดยทั้งสอง Skin เป็น expansion board ประกบเข้ากับ Micro Python board ให้มีความสามารถของการแสดงผล  LCD และ ภาคเสียง

ผ่านไปเจ็ดวัน ของก็อยู่ในมือ ส่งตรงจาก Cambridge UK โดยจ่าหน้าซองด้วยลายมือคนออกแบบเองเลย Damien George

หลังจากต่อสาย USB แล้วลองเล่นไปได้ สิบห้านาทีก็หลงรัก บอร์ด นี้เข้าจับใจ ความจริงตัวบอร์ดก็น่ารักดีครับ แต่ที่น่าสนใจกว่าตัวบอร์ดคือ การนำเอา Python มา compile เข้ากับ ARM Cortex-M ทำให้สามารถเขียน Python ได้ทันที่เมื่อเปิดเครื่อง เหมือนกับ ใช้ Apple Soft Basic เลย

เรื่อง MicroPython บอร์ดนี่คุยกันสามหน้าเจ็ดหน้าคงไม่จบ ไว้ค่อยว่ากัน ประเด็นบทความนี้อยู่ที่ Spec CPU ARM Cortex-M4 ตัวนี้คือตัวเดียวกับ development board STM32F4 ซึ่งผมก็มีอยู่บอร์ดนึง อีกทั้ง MicroPython ก็เป็น opensource project เลยเข้าไปดูใน source tree พบว่า นอกจาก George จะได้สร้าง build script (folder) สำหรับ Windows, Unix (สามารถ build และเขียน micro python บน Winodws, Linux และ OSX ได้) ยังสร้างสำหรับ stmhal (STM Hardware Abstraction Layer) ก็แน่นอน เพราะ MicroPython boad สนับสนุน CPU STM นี่นะ

อีกอย่าง ราคา MicroPython Board อยู่ที่ 29 ปอนด์ หรือประมาณ 1,500 บาท ในขณะที่ STM32F4-Disc อยู่ที่ 700 บาท (รุ่นไม่มีจอ touch screen)

เลยเกิดแรงบันดาลใจให้ลอง build micropython มาใช้บน STM32F4-Discovery ของตัวเองบ้าง โดยผมเริ่มจาก

เตรียม ARM Cross-Compiler
ผม build และทำงานต่อไปนี้ทั้งหมดบน UbuntuGnome 14.04 LTS นะครับ โดยติดตั้ง ARM cross-compiler ดังนี้

$ sudo apt-get install gcc-arm-none-eabi

จากนั้น clone micropython

$ git clone https://github.com/micropython/micropython.git

cd เข้าไปใน micropython/stmhal และเริ่ม build

$ make BOARD=STM32F4DISC

เครื่องที่ติดตั้ง UbuntuGnome ของผมเป็น Macbook white 2008 ใช้เวลา compile นาทีเดียวเองครับ :)

จากนั้นเราจะได้ file firmware.elf ใน folder stm/build-STM32F4DISC

ง่ายมาก แต่จะ download firmware.elf ยังไงล่ะ!!!

เตียมเครื่องมือสำหรับการ download firmware
สิ่งแรกที่ต้องการเลยคือ GDB สำหรับ ARM ให้ติดตั้ง package ดามนี้

$ sudo apt-get install -d gdb-arm-none-eabi

เครื่องผมมี gdb ติดตั้งอยู่แล้วเลยโดน reject ต้อง force install ด้วย

$ sudo dpkg -i --force-overwrite /var/cache/apt/archives/gdb-arm-none-eabi_7.6.50.20131218-0ubuntu1+1_amd64.deb

จากนั้นต้องใช้ tools สำหรับ upload เข้า STM board ผมใช้ project ใน github ชื่อ stlink clone มาแล้ว จัดการ build ได้เลยครับ

$ ./autogen.sh
$ ./configure
$ make
$ sudo make install

ส่วนเครื่องผมไม่ได้ลง autoreconf เลยต้องติดตั้ง

$ sudo apt-get install dh-autoreconf

พอตอนทำ configure พบว่าไม่มี libusb ก็ต้องติดตั้ง

$ sudo apt-get install libusb-1.0.0-dev

น่าจะครบกระบวนความในการ  compile stlink

มา download firmware กัน...
เชื่อมต่อบอร์ด STM32F4-Discovery ผ่าน USB (เป็นสาย Mini-B นะครับ อย่าต่อ OTG บน board STM32 จะมี USB สอง port หัวท้าย) เรียก

$ st-link

โปรแกรมจะรอ connection จาก GDB ให้เราเข้าไปที่ micropython/stmhal จากนั้น เปิด terminal ใหม่ขึ้นมาอีกหนึ่งจอ แล้วเรียก

$ arm-none-eabi-gdb build-STM32F4DISC/firmware.elf

พอเข้า CLI ของ GDB ก็ให้กำหนด target ตามนี้ครับ

(gdb) target extended localhost:4242

แล้ว สร้าง flash firmware ด้วย

(gdb) load

LED จะกระพริบอย่างสวยงามในระหว่างที่ firmware กำลัง flash เมื่อ LED ดับ ก็ terminate GDB แล้วดึงสาย USB ออกจากนั้นต่อสาย USB เข้าเครื่องใหม่ เราจะเห็น boot.py และ main.py แล้ว

เราสามารถ interactive กับ board ได้ด้วย REPL เหมือน Apple soft BASIC ด้วยวิธีดังนี้ครับ

$ sudo screen /dev/ttyACM0 

หลังจากต่อสาย USB แล้ว

เราจะพบว่า Micro Controller ไม่ใช่เรื่องยากอีกต่อไป ตอนผมได้ STM32F4-Disc มาครั้งแรก แล้วควานหา IDE หา compiler มาเขียนงานเล็กๆ ง่ายๆ นี่มันช่างวุ่นวาย ยุ่งยากเหมือลเกิน (ตอนหลังมาใช้ EWARM) แถมต้องจบที่ Assembly กับ C เท่านั้นอีก

MicroPython ก็แล้ว หรือเราจะลอง MicroLUA กันมั่งดีแมะ :p

ชอให้สนุกกับ micopython ครับ 

วันพฤหัสบดีที่ 10 กรกฎาคม พ.ศ. 2557

[เขียนเมื่อ] วันเกิด MS Internet Explorer [ครบ 15 ปี]

พบว่าตัวเองเคยเขียนเล่าเรื่องราวต่างๆ ของ Internet Explorer เมื่อปี 2010 (4 ปีที่แล้ว) ใน facebook ซึ่งเป็นเรื่องราวที่ผมมีส่วนเกี่ยวข้อง มุมมองส่วนตัว เลยเอา copy มาไว้ใน Blog เผื่อเพื่อนๆ อยากอ่าน (สนุกๆ)

  ผมจำได้ว่า เขียนบทความเป็นเรื่องเป็นราวลงแมกกาซีนรายเดือน ครั้งล่าสุดประมาณปี 2002 ได้ เป็นหนังสือ Windows Magazine ในเครือ AR เป็นบทความที่เกี่ยวกับ Mobile devices ที่ห่างหายและเลิกไปก้เพราะ บก. บริหารที่ติดต่อให้ผมไปเขียนบทความย้ายค่าย ไปอยู่ค่ายอื่น เลยไม่ได้ติดต่อกัน อีกอย่างตอนเขียนบทความนั้นเป็นช่วงที่กำลังพัฒนา ปลาดาวออฟฟิศ ด้วยเวลามีไม่มากนัก ทำให้เวลาจะส่งบทความ จะเขียนบนกระดาษสีน้ำตาลใหม้ (เผากันเลยทีเดียว) เช่นส่งวันจันทร์ คืนวันอาทิตย์เที่ยงคืนก็จะเริ่มเขียน แหะๆ  

พอดีวันนี้เปิดเวป blognone.com เจอข่าวครบรอบวันเกิด 15 ปี ของ Internet Explorer ทำให้รำลึกถึงวันวานกับ IE ที่ไม่ได้เป็นเพียงแค่ผู้ใช้ แต่ได้มีโอกาสเป็นส่วนหนึ่งของทีมพัฒนาด้วย โดยได้มีโอกาสพัฒนาในส่วนการแสดงผลภาษาไทย ทำให้อยากเล่าอยากเขียนอะไรเกี่ยวกับ IE ทิ้งไว้เผื่อใครจะสนใจ หรือเก็บเอาไว้อ่านส่วนตัวต่อไป ก็ไม่เลวนัก และคิดว่าคงไม่เขียนเป็นบทความแบบสมัยเขียนลง Windows Magazine เพราะนั่นน่ะ เขาให้หน้าละ 300 บาท เดือนนึงต้องลากสักสามสี่หน้า จะได้เงินกินขนมสักพันบาท  

ตอนที่ได้รับมอบหมายให้ทำ Internet Explorer เป็นภาษาไทยนั้น เป็นช่วงที่ Microsoft ของ Windows 95 Thai Edition SR1 (Service Released 1) ถ้าจำชื่อไม่ผิด รายละเอียด SR1 ทุกอย่างเหมือนของ US Edition แต่เพิ่มเติมในส่วนที่เกี่ยวข้องกับภาษาไทยเข้าไป เท่าที่จำได้มีดังต่อไปนี้   ปัญหา Fonts ทั้งหลายของไทย ตอน Windows 3.x ออกนั้น เรามี Fonts UPC ใช้ แต่พอ Windows 95 Thai Edition ออกมานั้น Fonts UPC ชุดเดิมยังมีอยู่ แต่ปัญหาก็คือ ชื่อดันเปลี่ยนจาก UPC ไปเป็น New ทำให้เอกสารไม่ compatible ก็แก้ปัญหา font substitute ให้กับ SR1  

ส่วนอื่นๆ ก็เป็นการ enable features ที่แนบมากับ SR1 ซึ่งก็คือ Internet Explorer 2.0 จริงๆ การมี IE ในตอนนั้น BillG (Bill Gates : aka billg@microsoft.com NOTE: ตอนอยู่ Microsoft Bill เคยส่งอีเมล์มาหา 2 ครั้ง (ตื่นเต้นๆ) "แต่" เป็นอีเมล์ที่ส่งมาขอบริจากเงินทำบุญ แล้วก็ส่งให้กับทุกคนใน Campus -_-' ) ไม่เคยเห็นด้วยเลย โดย Bill ให้เหตุผลว่า "Internet น่ะไม่มีทางประสบความสำเร็จหรอก แล้วที่มีนักวิเคราะห์ออกมาบอกว่า ในอนาคตโฆษณาตามหน้าแมกกาซีนจะมี Web Site URL ติดที่ทุกๆ หน้าโฆษณาน่ะเหลวไหล อนาคตมันต้อง MSN สิ" (ตอนนั้น Microsoft เปิดบริการ Online ของตัวเองชื่อ Microsoft Network เป็น Private Network โดยหมุน Modem เข้า service ของ Mcirosoft เอง เหมือนๆ กับ บริการ Compuserve ที่มี server อยู่ที่ Ohio) เราคงไม่ต้องบอกว่า Bill เป็นนักพยากรณ์ที่มีความสามารถขนาดไหน (เนอะ)  

พอ SR1 ออก ความจำเป็นของ IE ต่อการเป็นอยู่ของ Windows OS เริ่มมีมากขึ้น แน่นอนว่าตอนนั้น การแสดงผลภาษาไทยบน Web Browser ยังทำไม่ได้สมบูรณ์เลยนักตัวเดียว (ตอนนั้นใช้ Mozilla บน Windows 95)   มีนักพัฒนาไทยบางคนพยายามที่จะ hack ให้ Web Browser สามารถตัดคำภาษาไทย ด้วยวิธีการ Hook API ของ Windows Socket โดย API ใหม่ที่เขียนขึ้นจะเข้าไป replace API หลัก เท่าที่จำได้คือ function recv (TPC) แล้ว Monitor data stream ตัดคำแล้วแทรก <WBR> ระหว่างคำไทย ให้   ก็ว่ากันไป ในระดับหนึ่ง แต่ในเวลานั้น วิธีการนี้ถือว่า Advances ที่สุดแล้ว แต่ก็เป็นการ Work around ปัญหาเท่านั้น ให้ดียังไง ก็สู้การพัฒนาที่ตัวโปรแกรมตรงๆ ไม่ได้

IE 2.0 ไทย จำได้ว่า code name ของ IE 2.0 คือ "Ohare"  
Ohare เป็นชื่อสนามบินนานาชาติที่ Chicago และถ้ายังจำกันได้ Chicago เป็น code name ของ Windows 95 นอกจากนั้น ยังมี code name ของ project ย่อยๆ อีกหลาย projects ที่ใช้ code name ล้อกับ Chicago เช่น Rich Edit ซึ่งเป็น Windows extended native control ก็มี code name ว่า Caponeซึ่งเป็นชื่อของเจ้าพ่อ ขาใหญ่ Al Capone แห่ง Chicago นั่นเอง

   ตัว source code จำไม่ได้ ก็กว่าสิบปีแล้วที่ทำ ตอนนั้นทำกับ นนท์ (Weerapan Wilairat) ตอนนี้นนท์อยู่ทีม Windows Mobile น่าจะเป็นคนทำ UI Phone 7 กับทำ Kin (มั๊ง... ไม่ยอมบอกเล๊ย)ส่วนตัว IE มีอะไรบ้าง เดี๋ยวเล่าให้ฟัง เอาที่จำได้นะครับ พอดีเหมือสามสี่ปีที่แล้วเก็บบ้าน เจอ source code เก่าๆ (Hard copy print out) เก็บๆ ไว้บ้าง แต่ใช้อะไรไม่ได้แล้ว เพราะเป็นแค่บางส่วน เท่านั้น (น่าจะ 1 ใน 100 ของทั้งหมด)

ส่วน comment บนหัว source code นั้นส่วนมากจะ comment ตามนี้

/*Enhanced NCSA Mosaic from Spyglass
   "Guitar"

   Copyright 1994 Spyglass, Inc.All Rights Reserved

   Author(s):
   ชื่อนักพัฒนาที่รับผิดชอบ source file นั้นๆ โดยมี address (at)spyglass . com กันทุกคน
*/

   ใช่แล้ว Microsoft ซื้อ source code มาจกา Spyglass ไม่แน่ใจว่า Take Over Spyglass มาเลยรึเปล่า ช่วงนั้น MS นิยม Take Over มากเพราะมันสามารถพัมนาผลิตภัณฑ์แบบก้าวกระโดดได้ เช่น FrontPage Microsoft ก็ซื้อมา รู้สึกจะซื้อมาทั้งบริษัท แต่ปัญหาของ FontPage ช่วงแรกๆ คือ เวลา Edit HTML file แล้วชอบเพิ่มสิ่งที่พึงปรารถนาเช่น Tag แปลกๆ และที่สำคัญที่สุดคือเมื่อ save แล้ว HTML pattern เก่าที่ถูกเขียนไว้จะถูก FrontPage แก้มั่วไปหมด คนทำเวปจะหงุดหงิดกับ FrontPage มาก ถ้า HTML ไฟล์นั้นถูกพัฒนาด้วยมือมาก่อน แล้วนำมาแก้ด้วย FontPage  

   ช่วงนั้น HTML Editor ที่เป็นแบบ Visual ที่ได้รับความนิยมที่สุดคือ DreamWeaver ของ MacroMedia (ตอนนั้น ตอนนี้กลายเป็น Adobe ไปแล้ว) ด้วยเหตุผลง่ายๆ คือทำสิ่งตรงกันข้ามกับ FrontPage ที่กล่าวไว้ข้างต้น นั่นเอง   

   ตอนทำ IE 2.0 Thai Edition ใช้เวลาในการพัฒนาทั้งสิ้น 1 เดือน ทดสอบอีกกว่า 2 เดือน ทีมทดสอบคือทีมการตลาด และ PSS ของ Microsoft Thailand

สุดท้ายHappy Birthday MS-IE

Banner ยอดนิยม ใช้ promote IE ในช่วงแรกๆ

Flying windows ใน source เดี๋ยวนี้พัฒนาไปเยอะมาก ตอน 3.0 ทำเป็น e หมุนเป็นรูปโลก สวยมากๆ

หน้าตา Mosaic โดย Spyglass


วันอังคารที่ 8 กรกฎาคม พ.ศ. 2557

Upgrade LUA skill ด้วยการเขียน C module

มา upgrade LUA skill กัน ด้วยการเขียน LUA ไปเรียก module ที่เขียนด้วย C



ส่วนจะควรจะเรียกใช้ หรือเขียนอะไรด้วย LUA เขียนอะลัวด้วยไร ค่อยไปหาคำตอบกันเอง เช่น มีเพื่อนผมกลุ่มนึง เขียน Socket library เป็น Core module แล้ว Binding เขากับ LUA จากนั้นก็ใช้ LUA เขียน Server ทั้งตัว ก็มี

จาก Document พบว่า LUA ใช้ lua_State ในการส่งผ่าน parameters จาก LUA script ไปยัง C module ("Shared Object", file.so)  โดยมีรูปแบบมาตรฐานเริ่มต้นดังนี้

#include <lua.h> 
#include <lauxlib.h>

#include <lualib.h> 

int luaopen_cprime(lua_State *L){
   return 0;
}

ผมตั้งชื่อ library ตัวนี้ว่า cprime โดยวางแผนว่า ในนั้นจะมี function ง่ายๆ ในการตรวจสอบว่า ตัวเลขที่ส่งเข้ามาเป็น Prime Number หรือไม่ 

จากนั้นก็เพิ่ม function isprime สำหรับการตรวจสอบ Prime Number เข้าไป

static int isprime(lua_State *L) {
   int number = lua_tointeger(L, -1);
   for (int i = 2; i < number; i++) {
      if (number % i == 0 && i != number) {
         lua_pushnumber(L, 0);
         return 1;
      }
  }
  lua_pushnumber(L, 1);
  return 1;
}

อย่างที่ได้บอกไปแล้วว่า LUA ใช้ lua_State เป็น structure ในการส่งผ่าน parameters ระหว่าง LUA และ external module รวมถึงภายใน external module เอง 

ถ้าดูจาก function isprime ด้านบน lua_State จะเป็นทำหน้าที่ส่งผ่าน parameters ของ function isprime ทั้งหมด กรณีนี้ ผมส่ง integer เข้ามาตัวเดียว ซึ่งเราจะทำการดึง integer ที่ส่งผ่านเข้ามาใน isprime ด้วย lua_tointeger(L, -1);

lua_Integer lua_tointeger (lua_State *L, int index);

จากนั้นก็ตรวจสอบดูว่า ค่าที่ส่งเข้ามานั้นเป็น Prime Number หรือไม่ ถ้าใช่ เราก็ทำการ push 1 เข้าไปใน lua_State ด้วย lua_pushnumber(L, 0);

void lua_pushnumber (lua_State *L, lua_Number n);

ถ้าไม่ใช่ก็ push 0 เข้าไปแทน

แต่ก่อนจะใช้ isprime ได้ต้องทำการ register method isprime ให้ LUA รู้จักก่อนโดยการเรียก  (เราอาจเปลี่ยนชื่อ isprime เป็นอย่างอื่นก็ได้ ด้วยการแก้ที่ paraemters ที่สอง เช่น "fprime" เป็นต้น)

lua_register(L,"isprime",isprime); 

ใน luaopen_cprime(...); ซึ่งหน้าตาของ cprime.c จะเป็นแบบนี้

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

static int isprime(lua_State *L) {
  int number = lua_tointeger(L, -1);
  for (int i = 2; i < number; i++) {
    if (number % i == 0 && i != number) {
      lua_pushnumber(L, 0);
      return 1;
    }
  }
  lua_pushnumber(L, 1);
  return 1;
}

int luaopen_cprime(lua_State *L){
  lua_register(L,"isprime", isprime);
  return 0;
}

จากนั้น compile 

$ gcc -Wall -shared -fPIC -o cprime.so -I/usr/include/lua5.1 -llua5.1 cprime.c

(อย่าลืมติดตั้ง Lua5.1 ก่อนนะจ๊ะ)

แล้วเราก็มาสร้าง prime.lua file เพื่อเรียกใช้ isprime ของเรา หน้าตาจะเป็นแบบนี้ครับ

#!/usr/bin/lua

require("cprime")
print(isprime(13))
print(isprime(12))

แล้วเรียก

$ lua prime.lua
1
0

ประมาณนี้...ครับ :-)



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

foreach ใน C++

เพื่อนๆ ผมมักบอกว่า C++ โบราณแล้ว... ภาษามันเก่าแล้วพี่... C# เหอะพี่ (แหม่ อันหลังนี่...)



ถามกลับไปว่า เพราะอะไร ตัวอย่างที่มักจะยกกันขึ้นมา ก็คือ C++ มันไม่มี foreach นะพี่ (ผม) เฮ๊ย... มีนะ

ตอน C++11 ยังไม่ประกาศมาตรฐาน Qt ก็เคยทำ foreach มาให้ใช้

  QList<int> list;
 
  list << 1 << 2 << 3 << 4 << 5;
 
  foreach (int i, list)
  {
    qDebug() << i;
  }


พอ C++11 ประกาศใช้เราก็สามารถทำแบบนี้ได้

int arrayint[] = {1, 2, 3, 4, 5};
for (int i : arrayint) {
   ...
}

ทำแบบกำหนด boundary ของ container (cont) แบบนี้ก็ได้

for (auto it = cont.begin(); it != cont.end(); ++it)

หรือ จะแบบ reverse order ก็แบบนี้

for (auto it = cont.rbegin(); it != cont.end(); ++it)

เห็นแมะ C++11 โบราณตรงไหน :-)

วันอาทิตย์ที่ 15 มิถุนายน พ.ศ. 2557

เริ่มต้นกับ Openresty

เรื่องของเรื่องคือ มีเพื่อนผมคนนึงเขียน Openresty มาเป็นปี เคยเขียน MVC ให้ Openresty ด้วย แต่กลับมาบ่นๆ ว่า "พี่ๆ Openresty นี่ create project แยกกันยังไง คือผมอยากทำหลาย project บนเครื่องเดียวกัน"

อ้าว...สะงั้น เอางี้ผมบันทึกให้ แต่แชร์เพื่อนๆ ที่สนใจด้วยเลยละกัน...

หมายเหตุ : ผมใช้ OSX Maverick

ผมเริ่มจากเอา pcre verison ล่าสุด (ที่ผมใช้มัน 8.33 ไม่ล่าสุดหรอก) มา build ง่ายๆ ด้วย
$ ./configure
$ make
$ sudo make install

จากนั้น download source openresty มาจาก openresty.org untar แล้ว build
$ ./configure --with-luajit --with-cc-opt="-I/usr/local/include" --with-ld-opt="-L/usr/local/lib"
$ make
$ sudo make install

พอเวลาจะสร้าง application ก็เริ่มจาก สร้าง project folder

$ mkdir myproject
$ cd myproject
$ mkdir logs
$ mkdir conf

สร้าง nginx.conf ใน folder conf โดยเริ่มจาก default config แบบนี้ครับ

worker_processes  1;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
    server {
        listen 8080;
        location / {
            default_type text/html;
            content_by_lua '
                ngx.say("<p>hello, world</p>")
            ';
        }
    }
}

จากนั้น set path 

$ export PATH=/usr/local/openresty/nginx/sbin:$PATH

แล้ว start project 

$ nginx -p `pwd`/ -c conf/nginx.conf

แล้วใช้ browser เรียก http://127.0.0.1:8080

จะได้


หากต้องการสร้าง Project อื่นๆ อีก ก็สามารถทำแบบเดียวกันได้เลย โดยเริ่มจากการสร้าง Project folder

หากแก้ไข script conf หรือเพิ่ม lua ไฟล์ ต้องทำการ reload nginx ด้วย

$ nginx -s reload -p `pwd`/ -c conf/nginx.conf 



วันอาทิตย์ที่ 4 พฤษภาคม พ.ศ. 2557

บันทึก : ปลาดาวออฟฟิศ


เคยคิดที่จะบันทึกเรื่องราวความทรงจำเกี่ยวกับ ปลาดาวออฟฟิศ ที่ผ่านมาในชีวิตไว้เมื่อนานมาแล้ว แต่ตอนนั้นไม่รู้ว่าจะเขียนไว้ที่ไหน ตอนนี้มี Blog แล้วเอาลงไว้ตรงนี้ละกัน

เรื่องราวที่จะเขียนต่อไปนี้เป็นเรื่องราวที่เกิดขึ้นเมื่อสิบห้าปีกว่าๆ ที่แล้ว


ครั้งแรกกับ StarOffice
1998 ตอนนั้น...หลังจากตัดสินใจไม่ย้ายถิ่นฐานไปอยู่ Redmond ตามคำเชิญของเพื่อนร่วมงาน (Program Manager) ที่สนิทกัน (วันนี้ตอนนี้ก็ยังสนิทกันอยู่ใน facebook) ก็ตัดสินใจทำอะไรเองที่ กทม. หลายอย่าง เช่น สอบ MCP ไปสิบสองใบ เพื่อสมัคร MS Solution Provider เอามาขาย MS-Office

จริงๆ เขาให้สองแค่สองสามใบเองแหละ แต่สอบแล้ว ติดลม สนุกดีเลยสอบไปเรื่อยๆ

ตอนปลายปี 1998 ได้งานมางานหนึ่ง เป็นงานทำ Telephony gateway เพื่อประมูลโครงการ TOT ใช้การ์ด Dialogic ต่อ E1 (ตอนนั้นลาก RG58 ระหว่างสองเครื่องกันเอง) โดยเพื่อนสองคน คนนึงคือ จุ๊บ (เสียชีวิตแล้ว) อีกคนคือ วีรธร สุดท้ายโครงการนี้ก็ยุติลงเนื่องจากค่าเงินบาทตอนนั้นทำให้ต้นทุนสูงมาก ผู้ยื่นประมูลเลยรวมตัวกันขอยกเลิกโครงการ (มีแบบนี้ด้วย) แต่การ์ด Dialogic ผมยังซื้อไว้การ์ดนึงนะ จำได้ว่า ราคาสองแสนบาท

วีรธร เป็นหนึ่งในคนออกแบบการ์ดภาษาไทย Monochrome iRC หนึ่งในทีมพัฒนา MS-DOS Thai edition ปัจจุบัน ยังทำงานด้วยกันกับผมอยู่

ระหว่างที่เราพัฒนาโครงการ TOT ผมได้ข่าวที่ทำให้หงุดหงิดใจ (ช่วงนั้นของแรง อารมณ์ร้อน) ว่า SUN Microsystem ได้เข้าซื้อกิจการ StarOffice แล้วทำมาปั๊มแผ่น CD แจกในไทย ที่หงุดหงิดไม่ใช่เรื่องแจก StarOffice แต่หงุดหงิดเพราะ มันใช้ภาษาไทยไม่ได้ จะแจกทำไม? หลังจากได้ติดตั้งและทดลองใช้

วีรธร ที่มีเพื่อน (ลาดกระบัง) อยู่ที่ SUN ตอนนั้น ก็บอกว่า เดี๋ยวผมลองคุยกับเพื่อนดูเผื่อสนใจทำภาษาไทย จะได้มาจ้างคุณป้อทำ ผมตอบไปว่า อ๊ะๆๆ ไม่ได้อยากทำนะ... แค่บ่นเฉยๆ

นั่นคือฉากแรกที่ได้รู้จักชื่อ StarOffice

เริ่มเปิดบริษัท Algorithms
เป็นการเปิดบริษัทด้วยความบังเอิญ เพราะต้องวางบิลเก็บเงินค่าพัฒนาโปรแกรมระบบขายตั๋วหนังกับ EGV พอเปิดแล้วโครงการมันทำคนเดียวไม่ไหว ต้องหาคนมาช่วย พอมีคนช่วยพื้นที่ทำงานมันก็น้อยลง เลยต้องย้ายไปอยู่ที่อื่น แล้วก็มาจบที่ Software Park ตอนปลายๆ ปี 1999

ตอนนั้น เบื่อหน่ายอะไรหลายอย่าง พยายามต่อสู้ให้บริษัทอยู่ให้ได้ รับงานทุกอย่างเป็นต้นว่า
  • ทำเกมภาษาไทย ไม่ได้แปลภาษาอังกฤษเป็นไทยนะครับ เขาแปลมาแล้ว แต่เขาจะเอา source code มาพัฒนาการแสดงผลให้เป็นภาษาไทย เกมแรกที่ทำคือ Pharaoh ตามมาจนครบ Series เลยคือ Cleopatra, Zues และ Prosidon จากนั้นก็มี Title อื่นๆ อีก สิบกว่า Title ได้มาก Title ละแสน ทำกัน Title ละสองเดือนบ้าง ยากหน่อยก็สามเดือน ต้องรอส่วนแบ่งจากยอดขาย ถ้าขายเกิน 5,000 กล่อง จะได้ส่วนแบ่งกล่องละ 30 บาท (มั๊งจำไม่ได้) และก็ไม่เคยได้ เพราะได้รับแจ้งตลอดว่า ขายไม่ถึง 5,000 กล่อง (จบข่าว)
  • ทำภาษาไทย PocketPC ปัญหาเยอะ เยอะมากๆ ก็ไม่เอาดีกว่า ชีวิตสั้น หนีเลย ใครชวนทำอะไรเกี่ยวกับไอ้นี่ ไม่เอาแล้ว มีคนมาขอซื้อ source code ก็ไม่ขาย แพงเท่าไรก็ไม่ขาย ลบทิ้งไปเลย เกลียดแม่ง...
    • แต่ตอนหลังใจอ่อน ตอน ปีเตอร์กวง (ควงมือถือ) ชวนทำภาษาไทยบน Motorola Q8 (ตอนนั้นคุณกวงอยู่ Motorola) ใจอ่อนเพราะทำกับทีมที่ Chicago และทำลงใน ROM Q8 จากโรงงานเลย แถมทำในระดับ Platform ด้วย คือทำ ภาษาไทย localization แบบเต็มรูปแบบ แน่นอนรวมถึง Keyboard hardware ภาษาไทย (Q8 มี keyboard แบบ Blackberry)
ส่วน EGV เลิกโครงการไป

ปี 2000 มีข่าวว่า SUN จะเปิดเผย source code StarOffice
ผมมีโอกาสพูดเปรยๆ กับ อาจารย์ รอม ตอนนั้นมี อาจารย์ สมนึก คีรีโต รวมวงสนทนาด้วย อาจารย์รอม สนใจอยากให้ทำแล้วแจกในวันเปิด Software Park อย่างเป็นทางการ 

ตอนนั้น SUN มีแต่ข่าวจะเปิดเผย source code เวป openoffice.org ยังไม่มีนะครับ 

มันต้องใช้เงินทุนพัฒนานะครับ อาจารย์รอมเลยอาสาเอาโครงการนี้ไปเผยแพร่กับองกรค์หรือใครที่สนใจ สนับสนุน สุดท้ายมาจบที่ SUN Microsystem Thailand แจ้งความจำนงว่าจะเป็นเจ้าภาพเอง

ผมจำได้ว่า เข้าไปที่ SUN ตอนนั้นอยู่ที่ตึก อับดุลราฮิม สามครั้ง ไปฟังรายละเอียดความต้องการ ไปส่งเอกสารที่ทำ และ ไปฟังการตัดสิน

ตอนนั้น SUN มี พี่สุธรรม พี่ศิริวัฒน์ และ อาร์ต (Bact' นั่นแหละ) เป็น contact point ได้แจ้งว่า มีบริษัทให้ความสนใจทำโครงการนี้ทั้งหมด 12 บริษัท (จ๊ากกกก นี่มันโครงการที่เราเสนออยากทำนะ) วิธีการตัดสินว่าใครจะได้ทำคือ ให้แต่ละบริษัทเขียนมาว่า ถ้าได้ทำจะทำอะไรกับ StarOffice บ้าง

ใครเขียนอะไร ผมไม่รู้ (จะรู้ได้ไง เขาไม่บอก) แต่ผมเอา Specification ของ Microsoft Office 97 Thai Edition มายำใส่จนครบเลย ก็ผมร่วมเขียน Spec Office 97 Thai Edition ในตอนนั้น มันเลยจำได้....หมด... ตอนนี้ก็ยังจำได้นะเฟ้ย...

แล้วผมก็ได้ทำ วันที่รู้ว่าได้ทำ ก็รู้ที่ตึก อับดุลราฮิมนั่นแหละ 

Multiple Platform
อีกเรื่องที่เร้าใจมากตอนนั้นคือ ผมและทีมต้องพัฒนา StarOffice บน 3 Platform คือ Solaris, Linux และ Windows 

ทีมพัฒนา
ต้องยอมรับอย่างหนึ่งว่า การหาทีมที่สามารถเขียน C++ โครงการขนาดใหญ่ ไม่ใช่เรื่องง่าย และสุดท้าย Code C++ ทั้งหมด ผมก็เป็นคนรับผิดชอบ ทีมที่เหลือช่วย Build, Setup, test, localization จัดการ font, graphics เป็นต้น

เครื่องที่ใช้
เรามีเครื่องจำกัด ผมใช้เครื่องคนเดียวทั้งหมด 4 เครื่อง มี Linux 1, Windows x2 และ Solaris (Sparc) ตอนนั้นห้องทำงาน ถ้าไม่เปิดแอร์ นี่เหงื่อหยด และก็เหงือหยดทุกวันเพราะหลัง 6 โมง Software park จะปิดแอร์!!!

เรามี Server อีกสามเครื่อง ซึ่งเป็นเครื่องเช่าทั้งสิ้น ไม่มีปัญญาซื้อ ไม่มีเงินเหลือพอที่จะซื้อ (เครื่อง Solaris ตอนนั้น SUN ให้ยืมมา)

เสนอค่าใช้จ่ายในการพัฒนา
ผมเป็นโปรแกรมเมอร์ (+นักดนตรี) ที่เผลอลืมตัวไปเป็นผู้ประกอบการ คิดราคาค่าใช้จ่ายในการพัฒนา ก็คิดว่า มันคือ OpenSource ตอนนั้นยังไม่ชัดเจนตัวเองเท่าไรว่า Opensource คืออะไร ก็ใช้วิธีคิดว่า มันคือ Contribution ก็น่าจะคิดค่าพัฒนาพอดีๆ กับใช้จ่าย เลยเอาค่าใช้จ่ายคูณเดือน แล้วแจ้ง SUN ไป (น่ารักแมะล่ะ)

รายได้จึงพอดีกับค่าใช้จ่าย ผมเลยมักจะพูดติดตลก (ปนน้ำตา) สมัยนั้นว่า ผมทำน่ะไม่ได้อะไรนะ คนที่ได้อะไรคือคนที่ทำ หนังสือขายต่างหาก ผมไปร้านหนังสือ เห็นหนังสือ ปลาดาว เต็มแผง บอกตรงๆ ไม่เคยมีใครส่งมาให้ผมเก็บเป็นที่ระลึกเลย (หลังๆ มีส่งมาให้ สามสี่ Title :) )

ก่อนเริ่มงาน
พี่สุธรรม โทรมาบอกว่า มีนัดคุยกับ NECTEC เพื่อแจ้งให้ทราบว่าเราจะพัฒนา Office Thai เพราะตอนนั้น NECTEC มี Linux TLE เราไปคุยกันที่ตึกยิปซั่ม ได้เจออั้น กะเทพ ครั้งแรกที่นั่น

เริ่มพัฒนา 
ตอนได้ Code มา เป็นอาทิตย์แรกที่ OpenOffice.org เปิดตัว Code Build ไม่ผ่านเลยครับ ผมเลยกลายเป็นกลุ่มคนกลุ่มแรกๆ ที่ร่วมกันพยายาม Build เข้าใจว่า จริงๆ มัน build ผ่านแหละ แต่ Dev Team ที่ Humburg German commit ลง CVS ไม่สมบูรณ์ (อ้อ ตอนนั้นใช้ CVS ครับ)

ผมพยายาม Build อยู่เป็นเดือน พอ Windows ได้ Linux ก็ไม่รอด พอเอา Linux รอด โหดสุดก็ Solaris เพราะเครื่องที่ผมได้มาใช้เวลา Build 30++ ชั่วโมง ไม่นับถ้า Build failed นะครับ  (ผ่านมาได้ยังไง ยังนึกไม่ออกเลย)

พอ Build ออกมาได้ ก็พบว่า version ที่เราได้ code มาเป็น version ที่ OO.o ยังไม่ release คือ Bugs ตรีม นั่นแหละ 

ไม่ว่ากัน ผมทำหน้าที่ของผม จับยัด features ไทย อย่างเดียว มี code ใหม่ ก็จับ merge จำได้ละ มี Engineer ในทีมผมคนนึง คอยทำหน้าที่ merge อย่างเดียวเลย เรา merge มือนะครับ ผมจะ list ว่าผม touch file ไหน module ไหน แล้ว เข้าจะ fine merge จุดนั้นๆ ที่เหลือก็ replace ไป

ข้อดีของ OO.o คือมันเป็น Share library ผมทำ features ไทยที่เดียว มันจะใช้ได้ทุก application ย่อย อย่าง ตัดคำ ทำที่เดียว ใช้ได้ทุก app อันนี้ก็สะดวกไปอย่าง...

Release แรก
ตอนเรา Release แรก เรา Release ก่อนหรือหลัง OpenOffice.org วันนึงนี่แหละ ผมจำไม่ได้ แต่การ Release ใกล้กับ OO.o ตอนนั้น ไม่ใช่เรื่องดีเพราะ Code มันคนละ Code

คือ Code ของผมไม่ใช่ Code ล่าสุด เพราะผมต้อง build จาก code ที่อุดมไปด้วย Bug แล้วนำไป production คือปั๊ม CD นู่นนี่นั่นอย่างน้อยก็อาทิตย์สองอาทิตย์ ทำให้ version แรกของเรา Bug เต็มไปหมด

คาใจเรื่อง Opensource
ผมตั้งคำถามถามตัวเองตลอดในช่วงนั้น เครียดไปหมด และน่าจะเป็นเหตุผลหนึ่งที่ผมไม่อยากทำต่อคือ

โครงการนี้เป็นของ SUN ซึ่ง OpenOffice.org เป็น Dual-license ที่ SUN กำหนด เพราะเป็นของ SUN, SUN ซื้อ StarOffice มา หมายความว่า SUN จะทำอะไรกับมันก็ได้ จะ Close source จะ Open หรือจะลบทิ้ง แบบที่ผมลบ source code ทิ้งไปตลอดกาล หลาย projects ก็ได้ 

ปลาดาว ไม่ต่างอะไรกับ OpenOffice.org

แต่มักมีกระแสว่า Pladao ไม่ OpenSource 

ทัศนะคติผมกับวงการ Software ไทย ตอนนั้นเปลี่ยนไปเยอะครับ ผมว่าผมเป็นคนคุยไม่รู้เรื่องแล้วนะ ....แหม่...

ผมมักจะโดนด่าตาม Board ต่างๆ จนผมตั้งคำถามกับตัวเองตลอดเวลาช่วงนั้นว่า นี่ตกลงผมทำผิดพลาดมากใช่ไม๊ ที่ลุกขึ้นมาทำ Office Thai ตัวหนึ่ง จนโดนด่าขนาดนี้ ทำแล้วก็ไม่ได้ร่ำรวยเป็นเศษฐี ยังต้องหา Project อื่นทำต่อไป ต่อสู้ดิ้นรนต่อไป วันดีคืนดีไปเปรียบเทียบกับคนอื่นใกล้กัน เขาสบายกว่าเราอีก ยิ่งทุกข์

จนต้องหันหลังให้ เพราะมันทุกข์เกินไป... บนความไม่เข้าใจ จนป่วย ทั้งกายและใจ ถึงทุกวันนี้ 

1st OpenOffice.org Developer Conference
ผมไปรวมงาน OO.o DevConf  ครั้งที่หนึ่ง ที่ University of Humburg ในฐานะ Speaker ของ Session Thai Localisation เพื่อบอกให้ developer ของ SUN ที่ Humburg ทราบว่าเราพัฒนาภาษาไทย ยังไงตรงไหน code อยู่ไหน กรุณานำไปใส่ หรือประยุกต์ใช้ใน OpenOffice.org ด้วย 

ผลที่ได้ ผมไม่ได้ดูละเอียด แต่อย่างเรื่อง Sequence checking ภาษาไทยนี่มีสามอย่างให้เลือกเหมือนที่ผมเขียนไว้ใน Spec เลยคือแบบ 
  • เตือนเมื่อพิมพ์ Sequence ซ้ำ เช่น มี วรรณยุกต์แล้วพิมพ์วรรณยุกต์อีกตัว จะมีเสียงเตือน และไม่ทำอะไรต่อ
  • แทนที่ เช่นมีไม้เอกแล้ว พิมพ์ไม้โท ไม้โทจะแทนที่ไม้เอก, มีสระอุ แล้วพิมพ์สระอิ สระอุจะหายไป สระอิจะแทนที่
  • แบบไม่สนใจตรวจสอบ Sequence



พัฒนาต่อ
SUN ให้ทำ version 2 และ version 3 ของ ปลาดาวต่อ โดยในระหว่าง version 2 และ version 3 ผมได้มีโอกาสทำ SUN's Mad hatter ภาษาไทยด้วย
** Mad hatter เป็น Linux ของ SUN distro หนึ่ง
** Mad hatter เป็น code name ของ SUN's Java Desktop System


หันหลังให้ OpenOffice.org
ผมคิดเสนอว่า Software มันก็เหมือนเราวาดภาพลงบนผืนผ้าใบ มันต้องแน่วแน่ มันต้องมีจินตนาการ ต้องมีความเป็นเอกลักษณ์ และงดงาม 

ไม่ใช่สงคราม ไม่ใช่การต่อสู้ระหว่างใครกับใคร ถ้าจะต่อสู้ก็เป็นการต่อสู้กับตัวเอง 

ถ้ามันเป็นทุกข์ ผมจะเดินหนี 
ไม่มีเหตุผลอะไรที่จะทำให้ชีวิตเป็นทุกข์ 
แล้วมีเหตุผลอะไรที่ไปทำให้ชีวิตคนอื่นเป็นทุกข์

มีอะไรตั้งเยอะแยะที่ยังทำได้ และเป็นสุข บนโลก Software

ตกหล่นอะไร ค่อยเพิ่มเติมต่อนะครับ 

หมายเหตุ : ชื่อ ปลาดาว ออฟฟิศ อาร์ต (Bact') เป็นคนตั้ง มาจาก Star + ofFice (Fice ออกเสียง Fish) เลยเป็นปลาดาว :)



วันเสาร์ที่ 19 เมษายน พ.ศ. 2557

AngularJS + JSONP + Golang

เนื่องจากผมไม่ใช่คนเขียนเวปโดยธรรมชาติ ความรู้เรื่องเวปจึงน้อยมาก แต่ช่วงที่ผ่านมา เจองานที่ต้องใช้เวปในการนำเสนอข้อมูล จริงๆ คือทำ รายงานนั่นแหละ คือใช้ Crystal report ก็ได้แหละ แต่ปัญหาคือ ยิ่ง Crystal Report ยิ่งตายหยั๋งเขียด...

สุดท้ายบากหน้ามาหยุดที่ทำเวปให้ Query ข้อมูลแทน เนื่องจากโครงการที่ทำไว้นั้น server เป็น Golang RESTFul อยู่แล้ว (อ่านใน blog ก่อนหน้านี้นะครับ) เลยต่อยอดเลย

ผมเปิด port ใหม่
ไม่ใช่เรื่องอยากอะไรสำหรับ Golang ก็แค่ Go routine แต่ผมใช้  Sync ตามนี้

wg := &sync.WaitGroup{}
wg.Add(2)
go func() {
   server.StartREST()
   wg.Done()
}()
go func() {
   server.StartReport()
   wg.Done()
}()
wg.Wait()

ด้วย code ด้านบน ใน function StartREST() ก็จะไป ListenAndServe port 8080 ส่วน StartReport จะไป ListenAndServer port 8088 สำหรับ FileServer เลย

แค่นี้เราก็จะได้ server 2 ports มาใช้

ผมเลือก AngularJS มาทำ FrontEnd (ซ่า...ไม่เข้าเรื่อง) คือมันง่ายดีครับ เคยใช้ครั้งนึงตอนคิดจะทำ DropBox Clone (ตอนนั้นใช้ชื่อว่า DropBag แหะๆ เป็นงานทดลอง OpenResty + MongoDB's GridFS นะครับ) ผมเริ่ม code AngrulaJS ง่ายๆ เพื่อทดลองเรียก REST API ให้ส่ง JSON กลับมา Render พบว่า เกิดปัญหา Cross Domain Policy ขึ้น เนื่องจาก API ที่่เรียกอยู่อีก port ส่วนตัว Front-end ที่ใช้ Angular อยู่อีก port เพียงเท่านี้ก็ Cross Domain แล้วครับ ไม่ต้องถึงกับ คนละ IP

ประเด็นที่ทำให้ Blog นี้ขึ้นมา
ผมพยายามหาทางออกอยู่นาน ตอนแรกก็ย้าย Front end ไปไว้ที่ port เดียวกับ REST API ก็พบปัญหาเนื่องจาก ใช้ go-json-rest ของ Antoine แล้วต้องแก้ หลายอย่าง อีกทั้งยั้งมีพวก css, js ที่ต้องถูกเรียก load จาก browser อีกหลายไฟล์ ต้องแยกแบบเดิม แล้วใช้ JSONP

 รู้ว่า JSONP ใช้สำหรับแก้ปัญาเรื่อง CrossDomain หลายคนบอกไว้เช่นนั้น แต่ปัญหาคือ ผมไม่เคลียร์ ผมเริ่มหาข้อมูลว่า AngularJS  ใช้ JSONP ยังไง เรียก server แบบไหนยังไง สุดท้าย พบว่า มันต้อง Wrap JSON result ของเราด้วยชื่อ callback function ที่ front end ส่งไป แล้วส่งกลับมาในรูปแบบของ Javascript เช่น

http://192.168.1.1:8080/api/report.json?callback=jsonp_callback

ข้อมูลที่ส่งกลับจะต้องอยู่ในรูปแบบ

jsonp_callback([{"sequence":1,"data":"abc"},{"sequence":2,"data":"abc"}]);

ก่อนหน้านั้น REST API ของผมจะส่งข้อมูลกลับมาเป็น

[{"sequence":1,"data":"abc"},{"sequence":2,"data":"abc"}]

ทำไมต้องเป็นแบบนี้
มันคือการหลอก browser policy ว่ามันคือ javascript นะเว๊ย ไม่ใช่ data ไม่ผิด policy แล้วตัว framework ก็จะเรียกใช้ function jsonp_callback ซึ่งก็จะได้ข้อมูลที่ส่งจาก server มา

ได้ความเช่นนั้น ก็ลองค้นหาว่าพวกเรามีใครเคยเขียนๆ ไว้รึเปล่า พบว่ามี... คนถาม โดยคำตอบทั้งหมดคือ ไม่ต้องคำถาม ส่วนมากจะตอบว่าคือ JSON ซึ่งไม่ใช่

Implementation
เริ่มจาก server, Golang ใน function ที่ส่งค่า JSON กลับไปให้ Front-end ก่อนหน้าผมใช้ go-json-rest ด้วย

w.WriteJSON(&results) 

โดย results คือ array ของ struct

type result struct {
  Sequence int
  data string
}

w คือ *rest.Response

ผมเริ่มจาก Marshal results มาเป็น byte array (encoding) จากนั้น Query callback value จาก FormValue ที่ front end ส่งมา ซึ่งก็คือ callback value นั่นล่ะครับ แล้วเอามาต่อๆ กัน (concatenate) โดยทดลองแบบง่ายๆ ตามนี้

baJson, _ := json.Marshal(results)
strJson := r.Request.FormValue("callback")
strJson +=  "("
strJson += string(baJson)  // baJson เป็น byteArray
strJson += ");"
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write([]byte(strJson)) 

ผมลัพท์ที่ได้ จะมี callback wrap data ของเรามาในรูปแบบ javascript function

ส่วน front-end ผมหน้าตาแบบนี้ครับ เป็น AngularJS แบบง่ายๆ

index.html
<html ng-app="MyReport">
  <head>
    <script src="http://code.angularjs.org/1.2.9/angular.min.js" ng:autobind></script>
    <script src="/assets/js/myreport.js"></script>
  </head>
  <body>
    <div ng:controller="MyCtrl" id="Ctrl">
      <button ng:click="calldata()">call data</button><br/>
      <h2 ng-show="data">Data from callback</h2>
      <pre>{{data}}</pre>    
    </div>
  </body>
</html>

myreport.js
angular.module('MyReport', []);

function MyCtrl($scope, $http) {
  var url = "http://192.168.1.1:8080?callback=jsonp_callback";

  $scope.calldata= function() {
    $http.jsonp(url).then(
      function(s) { $scope.success = JSON.stringify(s); }, 
        function(e) { $scope.error = JSON.stringify(e); } 
      );
  }
}

function jsonp_callback(data) {
    var el = document.getElementById('Ctrl');
    var scope = angular.element(el).scope();
    scope.$apply(function() {
        scope.data = JSON.stringify(data);
    });
}

อธิบายได้ว่า พอ user click ที่ปุ่ม call data จะไปเรียก function $scope.calldata (อยู่ใน AngularJS controller scope MyCtrl) ก็จะไปเรียก $http.jsonp ที่เป็น method ของ AngularJS ใน url ที่เราส่งไปจะมี formvalue callback กำหนดให้ server wrap data มาในชื่อ function ที่เรากำหนด กรณีนี้ใช้ชื่อว่า jsonp_callback

เมื่อ result ถูกส่งกลับมาที่ front-end, javascript จะทำการ execute callback function jsonp_callback ซึ่งจะต้องอยู่นอก Angular's controller scope คือต้องเป็น execute global scope นั่นเอง

ปัญหาเลยเกิดต่อมาว่า แล้วจะจับ  data ส่งกับไปที่ controller scope ของ Angular ยังไง ก็เลยต้องกำหนด ID ของ ng:controller ขึ้นมา ("Ctrl") แล้ว getElementById จากนั้นก็ query scope จาก method ที่ Angular มีให้จะได้ scope แล้วทำการ $apply data เข้าไปที่ scope element ของ Angular อีกครั้ง (ดู function jsonp_callback นะครับ)

นี่คือ JSONP ซึ่งมันคนละเรื่องกับ JSON นะครับ


วันอังคารที่ 15 เมษายน พ.ศ. 2557

Web server in 3 lines with Golang

ผมเริ่มเขียน Golang ด้วยโจทย์ที่ต้องเขียน RESTFul Service สำหรับ  Enterprise mobile application เชื่อมกับ MSSQL ที่มีอยู่แล้ว บน Windows เลยเลือก Golang เพราะสะดวกในการ deploy สุด

คำถามที่ทำให้ตัดสินใจในการเลือกใช้ Golang ตอนนั้นก็คือ ใช้อะไรก็ได้ ที่พอ build แล้วส่งไปให้ support copy ไปวางบน server เรียกโปรแกรมแล้วใช้ได้เลย

Golang นี่แหละครับ ทำมาแล้ว...

พอเขียน Rest service จบ ก็เริ่มมี Requirement เพิ่มมาว่า อยากได้ report ก็วางแผนว่าจะทำเป็น Web page นี่แหละ ให้เลือกตัวเลือกที่จะทำรายงาน แล้วส่งไป query ผ่าน REST API ที่มีอยู่แล้ว ออกมาแสดงผล

ปัญหาคือจะเอาอะไรมาทำ file server?

ถ้าเลือก NGINX หรือ Apache เราคงต้องเป็นคนไปติดตั้งให้แน่ๆ Support เอาไม่อยู่

Martini
Golang web application framework ที่พึ่งเกิดและได้รับความนิยมอย่างรวดเร็จ น่าสนใจตรง Martini บอกว่า สามารถ support static file out of the box เลย คือเพิ่มไฟล์ใหม่เข้าไปปุ๊ป ก็เรียกผ่าน http ได้เลย --- น่าสนใจ ---

แต่งานที่ทำอยู่ใช้  go-rest-json ของ Antonie กับ default SQL framework ของ Go เอง แค่นี้ก็ทำงานได้ดีอยู่แล้ว และด้วยความฝักไฝ่ใน Benchmark, Go pure นี่ทิ้ง Revel ขาดเลยนะครับ แล้ว Martini ทำความเร็วสู้ Gorlilla ไม่ได้ด้วย เลยสองจิตสองใจที่จะใช้ Martini

ตัดสินใจเขียนเอง
ต้องยอมรับว่า เอาเข้าจริงๆ ที่ผ่านมาผม focus ที่ project มากจนไม่ได้ดู net/http ว่าทำอะไรได้ขนาดไหน วันนี้เลยไล่ดูหน่อยพบว่า

จริงๆ แล้ว net/http นี่มันสมบูรณ์แบบนะครับ ที่ว่ากันว่า ไม่ต้องใช้ external framework นี่เรื่องจริง

เลยลองเขียน static file server ออกมาดูได้ตามนี้

package main
import "net/http"
func main()  { http.ListenAndServe(":8080", http.FileServer(http.Dir("/Users/McDuck/Home"))) }

วันอังคารที่ 25 มีนาคม พ.ศ. 2557

C, Token-parsing preprocessor

ระหว่างปี 1995-1997 เป็นช่วงเปลี่ยนถ่ายจาก 16bit เป็น 32bit ในโลกของ Windows (ก่อนเป็น Win32) หลายท่านอาจจำได้ ปัญหาใหญ่ๆ ของ "C" โปรแกรมเมอร์หลายคนก็คือ การนำ Code เก่า หรือ DLL เก่าที่เป็น 16bit port มาเป็น 32bit ถ้ามีเวลาก็ค่อยๆ port โดยเริ่มจาก GlobalAlloc, GlobalLock, GlobalFree etc. แทนที่ด้วย malloc, free แบบที่ใช้ในปัจจุบัน

หลายคนที่มี code ใหญ่ๆ หรือมี DLL ที่ไม่มี code ไม่สามารถ port ได้ แต่ต้องการทำ Application ให้เป็น Win32 ก็มีทางเลือกอีกทางคือการใช้ Thunk layer ในการ เรียก 16bit API ด้วย 32bit app หรือ 16bit app เรียก 32bit API (อย่างหลังนี่ไม่ค่อยเห็น)

ซึ่งผมก็เป็นหนึ่งในนั้น....

เรื่องที่จะเล่าไม่ใช่เรื่อง Thunk layer หรือ Interoperability ของ Win16 กับ Win32 แต่เป็น Technique ที่ผมทำเอามาจาก Microsoft Excel source code (95) มาใช้กับการสร้าง Thunk layer ณ. เวลานั้น

Technique ที่ว่าคือ การทำ Token-parsing preprocessor, ซึ่งมีประโยชน์มากในการ define ชื่อ API ที่ทำให้เราสามารถจัดกลุ่มของ API ที่จะเรียกใช้เป็นหมวดหมู่ได้ง่าย (ดูตัวอย่างครับ)

#include <stdio.h>

#define GROUP_FIRST     _1_
#define GROUP_SECOND    _2_
#define TYPE_16         16
#define TYPE_32         32

#define METHOD_HELPER(a, b, c) a##b##c
#define METHOD(a, b, c) METHOD_HELPER(a, b, c)

int method_1_16(int x) {
   printf("method_1_16 : %d\n", x);
}

int method_2_16(int x) {
   printf("method_2_16 : %d\n", x);
}

int method_1_32(int x) {
   printf("method_1_32 : %d\n", x);
}

int method_2_32(int x) {
   printf("method_2_32 : %d\n", x);
}

int main() {
   METHOD(method, GROUP_FIRST, TYPE_32);
   METHOD(method, GROUP_SECOND, TYPE_16);
   return 0;
}


Happy hacking!!! :)

วันเสาร์ที่ 1 มีนาคม พ.ศ. 2557

กลับมาหา ภาษาที่คุ้นเคย, C++

ในที่สุดก็ต้องกลับมาภาษาที่คุ้นเคย C/C++ ดีใจได้เขียนแบบจริงๆ จังๆ อีกครั้ง โครงการดีด้วย...

จะว่าไป C/C++ เป็นภาษาเดียวในชีวิตที่ไม่ต้องเปิดคู่มือ (นอกจาก features ใหม่ๆ บางตัวใน C++11)

ทำให้สะท้านเหมือนกัน ตอนเรียน Go (lang) ก็ชอบวิธีการ assignment อย่าง
X := 12 // X เป็น Integer
ใน C++11 ก็มี
auto X = 12; 

ตอนปี 2008 ผมเคยซื้อ Library Qt ตอนนั้น พึ่งจะเป็น Release version 4.0 มีลูกค้าจ้างพัฒนาโปรแกรมสอง Platform ตือ OSX และ Windows ตัดสินใจจ่ายค่า Library ให้กับ Troll ไป เกือบสองแสนบาทสำหรับ สอง Platform 

สถานะ Qt ตอนนี้ มันสุดยอดจริงๆ นอกจากเขียนบน Linux, OSX, Windows ได้แล้ว ยัง Cross compile ไป iOS และ Android ได้อีก 
Android นี่สะดวกกว่าเพื่อน เขียนได้ทั้งบน Linux, OSX และ Windows เลย ส่วน iOS ต้องเขียนบน OSX

ตอนนี้ได้กลับมาใช้อีก ดีจัง (ตังค์อยู่ครบ)

แเละเหมือนเดิมคือ Oracle Call Interface สำหรับ Qt (QOCI) ไม่สามารถใช้ได้ ต้อง Build เอง เพราะต้องไป download OCI Library และรับทราบ License agreement ของ Oracle ก่อน 

วิธี Build QOCI บน OSX

download instanceclient มาจาก Oracle.com ตาม blog ก่อนหน้านี้ http://nanusorn.blogspot.com/2014/02/build-oracle-call-interface-golang.html

เวลา install Qt 5.2.x จะอยู่ที่ ~/Qt5.2.1 ให้ change directory ไปที่ ~/Qt5.2.1/5.2.1/Src/qtbase/src/plugins/sqldrivers/oci

$ export PATH=$PATH;~/Qt5.2.1/5.2.1/clang_64/bin

เรียก qmake สร้าง Makefile ใน folder ~/Qt5.2.1/5.2.1/Src/qtbase/src/plugins/sqldrivers/oci
$ qmake -o Makefile

แก้ Makefile.Release และ Makefile.Debug
โดยเพิ่ม -I/usr/lib/instantclient_11_2/sdk/include ที่ท้ายบรรทัด INCPATH
และ
เพิ่ม LFLAGS        += -L/usr/lib/instantclient_11_2 -lclntsh ใต้บรรทัด LFLAGS

แล้วเรียก 
$ make

วิธีนี้ง่ายสุด เร็วสุด ไม่ต้อง re-build Qt ทั้งตัว...

---------------------------------------------------------------
ถาม : กลับมาเขียน Qt เพื่อ?
ตอบ : เพื่อต้องการใช้ C++ เขียน Consumer สำหรับ rabbitMQ 

ถาม : แค่นี้หรือ?
ตอบ : ป่าวจะเอาไปเขียน Client Application บน Cubieboard ด้วย

ถาม : แล้วอย่างอื่นล่ะ?
ตอบ : ตอนนี้สนใจเอามาใช้พัฒนา Web Application ด้วยนะ เช่น
ตอบจริงๆ : ที่ว่ามาน่ะ ตอบเล่นๆ :p แต่เล่นจริงๆ นะ ปี๊ดส์ เลย



วันจันทร์ที่ 17 กุมภาพันธ์ พ.ศ. 2557

Build : Oracle Call Interface สำหรับ Golang

คืออยากใช้นะครับ แต่แหม่... มีแค่ https://github.com/mattn/go-oci8 ที่ดูดีสุด แต่คนเขียนเหมือนจะเขียนแล้วหนีไปแอบที่ไหนไม่รู้ ไม่บอกอะไรเลย

อยากใช้ก็ต้อง งม.. สิครับ

OCI ก็ต้องใช้ downlaod instance client มาก่อน (ตามสูตร) ลงทะเบียนแล้ว download จาก http://www.oracle.com/technetwork/topics/intel-macsoft-096467.html ผมใช้ OSX (Maverick)

Download

  • instantclient-basic-macos_x64-11.2.0.3.0.zip
  • instantclient-sdk-macos_x64-11.2.0.3.0.zip
แล้วผมทำแบบนี้ครับ
  • unzip basic แล้ว move ไปไว้ที่ /usr/lib/instantclient_11_2
  • sym link 
    • ln /usr/lib/instantclient_11_2/libclntsh.dylib.11.1 /usr/lib/libclntsh.dylib.11.1
    • ln /usr/lib/instantclient_11_2/libocci.dylib.* /usr/lib/libocci.dylib.*
    • ln /usr/lib/instantclient_11_2/libociei.dylib /usr/lib/libociei.dylib
    • ln /usr/lib/instantclient_11_2/libnnz11.dylib /usr/lib/libnnz11.dylib
  • ดูว่าเครื่องติดตั้ง pkg-config รึยัง ถ้ายังติดตั้ง ถ้ายังใช้ MacPort ก็ได้ครับ
    • sudo port install pkgconfig
  • แล้วสร้าง oci8.pc ไว้ที่ /usr/lib/pkconfig ตามนี้ครับ (ไฟล์จากเครื่องผม)
    • prefix=/usr/lib/instantclient_11_2
      libdir=${prefix}
      includedir=${prefix}/sdk/include/

      Name: OCI
      Description: Oracle database engine
      Version: 11.2
      Libs: -L${libdir} -lclntsh
      Libs.private: 
      Cflags: -I${includedir}
  • เริ่ม  Build 
    • ไปที่ project workspace path 
    • $ export GOPATH=`pwd`
    • $ go get github.com/mattn/go-oci8
    • Go จะ Build และ link OCI ให้อัตโนมัติ โดยใช้ oci8.pc ที่เราสร้างไว้
  • ถ้าไม่มีอะไรผิดพลาด (error report) ถือว่าเสร็จเรียบร้อย
  • ทดสอบ
    • เข้าไปที่ $GOPATH/src/src/github.com/mattn/go-oci8/example
    • แก้ไฟล์ oracle.go บรรทัด 
      • db, err := sql.Open("oci8", "scott/tiger")
      • เป็น config ของ ORA ของเรา
      • ของผมเป็นแบบนี้ครับ
      • db, err := sql.Open(
        "oci8", "system/manager@192.168.1.40:1521/hello")
    • เรียก $go run oracle.go

ไม่พบ Error เสร็จ เรียบร้อย...
Modified และ Fix error จาก : https://github.com/Centny/Centny/blob/master/Articles/How%20build%20github.com:mattn:go-oci8.md 

เขียน Windows Services ด้วย Golang

ช่วงปีหลัง หนีไม่พ้นต้องเขียนอะไรที่เกี่ยวกับ Windows อย่างหลีกเลี่ยงไม่ได้ พอจะตัดสินใจใช้ Golang คำถามที่ตามมาคือแล้วถ้าต้องเขียน services ล่ะ ใช้ golang ได้รึเปล่า

คำตอบวันนี้คือ "ได้"

ผมเอา code จาก RESTful ของ Blog ที่แล้วมาใช้ทำ service โดยการ compile code บน windows ให้เป็น .exe ก่อน

> go build restmain.go

จะได้ restmain.exe จากนั้นผมใช้ NSSM - the Non-Sucking Service Manager (มาพร้อม source code และ execute binary) unzip แล้ว

> nssm McDuckService [path to]restmain.exe

เรียบร้อยลองเปิด service management อ่า... McDuckService มาแล้ว Double Click แล้ว Start เลย... 


[เพิ่มเติม]

  • เวลาจะลบ service ออกใช้
    • > sc delete McDuckService
  • command line ที่เกียวกับ service ต้องเป็น administrator right 
  • อ้างอิง http://sanatgersappa.blogspot.com/2013/07/windows-service-with-go-easy-way.html
    • เขียนก่อนผมเกือบปี ใช้ nssm เหมือนกันเลย





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

ลองเขียน REST API ด้วย Go (with framework)

ก่อนนี้ลองเขียน web application ด้วย pure Go มาแล้ว และได้เห็นพิษสงของ Go พอสมควร คราวนี้ขอลอง REST API สักหน่อย แต่อยากได้แบบ ง่ายมากๆ ประมาณ KISS เลย (Keep It Siimple & Studip) เลยขอลองใช้ Framework ในตลาดดูบ้าง

ผมเลือก http://github.com/ant0ine/go-json-rest มาลองเล่นเป็นตัวแรก เริ่มเลย...

สร้างไฟล์ชื่อ restmain.go (หรืออะไรก็ได้)
แทรก import ก่อน

import (
   "net/http"
   "github.com/ant0ine/go-json-rest"
)

แล้วสร้าง func main

func main() {
handler := rest.ResourceHandler{}
handler.SetRoutes(
rest.Route{"Get", "/users/:id", GetUser},
)
http.ListenAndServe(":8080", &handler)
}

อธิบายได้ว่า ประกาศ handler ด้าย rest ของ go-json-rest แล้ว SetRoutes ไปที่ url ที่เราต้องการ จากนั้นยัดเข้าท่ามาตรฐานของ net/http package ของ Go

ไปดูที่ handler กัน

func GetUser(w *rest.ResponseWriter, req *rest.Request) {
user := User{
Id:   req.PathParam("id"),
Name: "McDuck",
}
w.WriteJson(&user)
}

ทดลองง่ายๆ ด้วยการส่งอะไรมา (ผ่าน url parameter) ก็จัดการส่งกลับ (ด้วย json format) เราก็ต้องสร้าง struct สำหรับการเก็บข้อมูลก่อนแปลงเป็น json

type User struct {
Id   string
Name string
}

แล้วก็ assign (:=) user โดยมี Id = ค่า id ที่ส่งผ่านมาทาง url parameter และ name (อันนี้ใส่เอง) จากนั้นใช้ res.ResponseWriter เขียน Json กลับไปที่ resquester

ก่อน run set GOPATH ก่อน (path เดียวกับ ที่เราเขียน code ด้านบน...)
$ export GOPATH=`pwd`

(windows platform ไปหาทางเอาเอง นาทีนี้กำลังงอล Windows,  Hang อยู่ได้ แม่ม...)

จากนั้นเรียก
$ go get .
go จะ download source มาจาก github.com/ant0ine/go-json-rest ไว้ที่ src ให้เรา จากนั้น
$ go run restmain.go

แล้วเปิด browser ไปที่ http://localhost:8080/users/1001100
Result :

{
  "Id": "10010",
  "Name": "McDuck"
}

[ง่ายดีจังตังค์อยู่ครบ]

source restmain.go

//-------------------------------------------------------------
package main

import (
 "github.com/ant0ine/go-json-rest"
 "net/http"
)

type User struct {
 Id   string
 Name string
}

func GetUser(w *rest.ResponseWriter, req *rest.Request) {
 user := User{
  Id:   req.PathParam("id"),
  Name: "McDuck",
 }
 w.WriteJson(&user)
}

func main() {
 handler := rest.ResourceHandler{}
 handler.SetRoutes(
  rest.Route{"Get", "/users/:id", GetUser},
 )
 http.ListenAndServe(":8080", &handler)
}

//-------------------------------------------------------------

วันพุธที่ 12 กุมภาพันธ์ พ.ศ. 2557

Tough Week : Oracle ODBC บน Linux

เป็นอาทิตย์ที่ยากลำบากมากช่วงหนึ่งแต่ก็ผ่านมาได้ จนต้อง blog ไว้เผื่อมีประโยชน์กับ ตัวเองและคนอื่น :)

กว่าสองเดือนที่ผมได้เขียน Server สำหรับการทำ Semi-Realtime transaction processing server (ตั้งชื่อให้มันยากๆ ไปงั้นแหละ) โดยใช้ Erlang ในการพัฒนา โจทย์ก็คือต้องเก็บ transaction ที่ส่งผ่านเข้ามาเป็น XML จากนั้น Encode XML เก็บเข้า Oracle แล้ว Archive XML ไว้ใน Document Base สักตัว (ผมเลือกใช้ MongoDB)

แรกๆ ใช้ Erlang OCI (Oracle Call Interface) ก็ดูดี แต่พบว่า มีปัญหาหลายส่วน จึงตัดสินใจเปลี่ยนจากการ code โดยการ generate SQL statement ใน Erlang แล้วยิง Statement ผ่าน OCI มาเป็นการเรียกน Stored Procedure ผ่าน ODBC แทน

Code เสร็จแล้ว ถึงเวลาต้อง Deploy แล้ว... ผมเลือก Debian 7.4 ที่ต้องติดตั้งเพิ่มก็มี

  • Erlang (ใช้ R16B03-1) ติดตั้ง package เพิ่มเติมตามนี้
    • sudo apt-get install build-essential libncurses5-dev m4
    • sudo apt-get install openssl libssl-dev
    • sudo apt-get install unixodbc-dev
  • git
  • mongodb (ใช้ repo ของ 10 gen : http://docs.mongodb.org/manual/tutorial/install-mongodb-on-debian/ )
  • และ ODBC 
ปัญหาก็คือ Instance Client ของ Oracle (download จาก http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html ) support .rpm ผมใช้ alien แปลง .rpm เป็น .deb ซึ่งก็ติดตั้งได้ แต่ไม่สามารถ connect กับ server ได้ เลยเปลี่ยนไปจาก Debian  ไปเป็น CentOS แทน เพราะไม่รู้ว่า ปัญหาจริงๆ คืออะไร

ย้ายบ้านมา CentOS (ขั่วคราว)
ข้อดีของ VMWare คือตรงนี้แหละ อยากลอง Distro ไหนง่ายนิดเดียว
ติดตั้ง CentOS เสร็จ ไม่ต้องคิดมาก download rpm จาก Oracle's instance-client site page มาเลย ไม่ต้องเยอะเอาแค่
  • oracle-instantclient12.1-basic-12.1.0.1.0-1.x86_64.rpm 
  • oracle-instantclient12.1-sqlplus-12.1.0.1.0-1.x86_64.rpm
  • instantclient-odbc-linux.x64-12.1.0.1.0.zip 
แล้ว $sudo rpm -ivh oracle-instanceclient12.1-basic-12.1.0.1.0-1.x86_64.rpm ไปก่อน ตากด้วย $unzip  instantclient-odbc-linux.x64-12.1.0.1.0.zip แล้ว copy libsqora.so.12.1 ที่ได้จากการ unzip ไปไว้ที่ /usr/lib/oracle/12.1/client64/lib

จากนั้นติดตั้ง unixODBC 
$sudo yum install unixODBC

export environment 

$ export ORACLE_HOME=/usr/lib/oracle/12.1/client64
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib

$ export TWO_TASK=//xxx.xxx.xxx.xxx:1521/listener
(note : listener ต้องตรงกับ server นะครับ)

note : ผมสร้าง oracle.conf ที่มี 
/usr/lib/oracle/12.1/client64/lib
หนึ่งบรรทัด ไว้ที่ /etc/ld.so.conf.d แล้วเรียก 
$ sudo ldconfig
หนึ่งที ด้วย :D

จากนั้นก็ไป copy tnsnames.ora มาจาก server (server ผมเป็น windows 7) เอามาจาก c:/app/[username]/product/11.2.0/dbhome_1/network/admin (ลองไล่ๆ ดูนะครับ แต่ละเครื่องไม่เหมือนกัน อยู่ที่การติดตั้ง)
ผมแก้ tnsnames.ora นิดหน่อยครับ เหลือแต่ 


_SID_NAME_ =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)
        (HOST = xxx.xxx.xxx.xxx)
        (PORT = 1521)
      )
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = _SID_NAME_)
    )
  )

_SID_NAME_ ตรงกับ SID ที่ server 
ไปตามอ่านใน oracle document พบว่าให้สร้าง tnsnames.ora ไว้ที่ $ORACLE_HOME/network/admin ก็ไปสร้างไว้ตรงนั้นตามเอกสาร 

จากนั้นสร้าง ODBC's drive profile ใน /etc/odbcinst.ini
ตามนี้ครับ 

[OracleODBC-12g]
Description = Oracle ODBC driver for Oracle 12g
Driver64 = /usr/lib/oracle/12.1/client64/lib/libsqora.so.12.1
FileUsage = 1
Driver Logging = 7

เน้นนะครับ : Driver64 เพราะผมลง x86_64 ถ้า google จะเจอแต่ Driver เฉยๆ (งมตั้งนาน) 
แล้วชี้ไปที่ libsqora.so.12.1 ที่ได้จากตอนที่เรา unzip ตอนต้นๆ

จากนั้นสร้าง DSN ใน /etc/odbc.ini ตามนี้ครับ

[simple]
Driver = OracleODBC-12g
DSN = OracleODBC-12g
ServerName = _SID_NAME_
UserID = _USER_
Password = _PASSWORD_

Note : Driver ต้องตรงกับที่เรากำหนดไว้ใน odbcinst.ini, ServerName ตรงกับ _SID_NAME_ ใน tnsnames.ora ส่วน _USER_ _PASSWORD_ ก็ตรงกับ oracle server ที่เราตั้งไว้

ทดสอบ....
ทดสอบ DSN ที่ส้รางขึ้นด้วย 
$ isql simple
simple คือชื่อ DSN ที่เราสร้างขึ้นใน odbc.ini

ผลลัพท์
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+

ผมเจอ error ไม่ต่ำกว่า 4-5 ตัว (แต่ละตัวทำเอามึน) เช่น
Error ORA-12154 :  TNS:could not resolve the connect identifier specified
อันนี้ตำแหน่ง tnsnames.ora อยู่ผิดที่ 

Debug ด้วย Log
หากพบปัญหาตอนเรียก isql ให้ใส่  -v เพื่อดู ORA error number แล้ว google error number ดู ถ้ายังไม่ได้อีก ผมใช้ strace ตามนี้

$ strace -fac -o isql.log isql -v simple

เช่นตอนกำหนดตำแหน่ง tnsnames.ora ผิด ผมเปิด isql.log พบว่า นั่นไง by default isql จะเปิด tnsnames.ora จาก 
"/home/[user]/.tnsnames.ora"
"/etc/tnsnames.ora"
"/usr/lib/oracle/12.1/client64/lib/network/admin/tnsnames.ora"
เป็นต้น

การใช้ sqlplus64 ตามด้วย custom tns connect ORA server ได้ ไม่ได้หมายความว่า ODBC จะ connect ได้ เช่น 
$ sqlplus64 user/password@//XXX.XXX.XXX.XXX/_SID_NAME_
connect ได้ query ได้ แต่ isql ไม่ works

การติดตั้ง sqleveloper Connect ORA server ได้ ไม่ได้หมายความว่า ODBC จะ connect ได้ เช่นกัน

อาจไม่ cover ทุก error ที่เจอ แต่ใคร install Oracle ODBC Driver บน Linux แล้วพบปัญหาลองแลกเปลี่ยนกันดูครับ 

ผมยังใหม่กับ Oracle มาก :D

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

ISO8601 TimeZone กับ Oracle PLSQL

ตัดสินใจส่ง XML เข้าไป Parse ใน Oracle procedure แทนที่จะ Parse ด้วย Erlang แล้วส่งไป ซึ่งก็น่าจะดีกว่า (หันไปเขียน PL อย่างเดียว)

แต่พอ Extract XML มาก็เจอปัญหา ISO8601 ที่ก่อนนี้ผมใช้ Erlang iso8601 library parse ออกมาแล้ว convert เป็น Oracle DateTime format ก่อนนำไปยัดเข้า Statement แล้วส่งผ่าน ODBC/OCI

แต่พอเป็น PL เหมือนต้องเรียนใหม่ (เหมือนเขียน Pascal เลยนะ) หาทาง Convert ISO8601 ที่ Generate มาจาก .net ไม่เจอ เพราะ format ที่มาเป็นแบบนี้

2013-08-30T14:26:58.1640094+07:00

ค่อยๆ ไล่ที่ละตัวจนได้ format

yyyy-mm-dd"T"hh24:mi:ss.ff7

ด้วย function TO_TIMESTAMP ของ PL

แต่ TimeZone นี่สิยังไม่ผ่าน อ่านในเอกสารพบว่า

ถ้าเป็น named timezone เช่น
CET : Central European Time
PST : Pacific Standard Time
ให้ใช้ TZR

ถ้าเป็น +/- เวลาเช่น
+07:00 ประเทศไทยเรา
ให้ใช้ TZH:TZM

แต่ลองใช้ยังไงก็เจอ ORA-01821 : date format not recognised

สุดท้าย พบว่า ต้องใช้ TO_TIMESTAMP_TZ ก็เสียเวลาปีสามชั่วโมงเต็มๆ

TO_TIMESTAMP_TZ('2013-08-30T14:26:58.1640094+07:00',  'yyyy-mm-dd"T"hh24:mi:ss.ff7TZH:TZM');


Erlang เป็นญาติกับ Prolog, ยังไง?

ก็เลยลอง Port Erlang มาเป็น Prolog สนุกๆ จะได้เห็นว่า จริงๆ แล้ว Erlang ไม่ไช่ภาษาที่คิดใหม่ทั้งหมดนะ

 Erlang
-module(mathStuff).
-export([factorial/1]).

factorial(0) -> 1;
factorial(N) -> N * factorial(N-1).


Prolog
:- module(mathStuff).
:- export  factorial/1.

factorial(0, 1).
factorial(N, R) :-
R is R * factorial(N-1).


วันจันทร์ที่ 20 มกราคม พ.ศ. 2557

กับดัก iso8601 ของ .Net

งมกับ DateTime ทั้งวัน Insert Record เข้าไปแล้ว Data ก็เดียวกัน ทำไม Query กลับมาไม่เจอ ผีหลอก!!!

ตอน Insert นี่ผมใช้ Erlang parse iso8601 มาเป็น Tuple แล้วใช้ ErlSQL จัดการ statement โดย parse DateTime format ที่ได้มาจาก XML ที่สร้างจาก .NET ตัวอย่าง

2013-08-30T14:26:58.1620093+07:00

ISO8601 ใน Erlang Parse ออกมาได้เป็น {{2013,8,30},{7,26,58}} โดยเอา +7:00 มาคิด ลดเวลาให้เป็น GMT +0:00 ซึ่งก็น่าจะถูก 

เวลาที่ได้คือ 7:26:58

แต่ Code .NET หาไม่เจอ!!!! ฉิบหายละ!!!!

.NET อ่าน XML file เดียวกัน Data เดียวกัน แต่ไม่ใช้ +7:00 มาคำนวน จาก XML ได้เป็น 14:26:58 ไม่เอา +7:00 มาคิด!!!!

ถ้า Iso8601 ของ Erlang ถูก...ซึ่งผมต้อง port .NET service มาเป็น Erlang แล้วข้อมูลเดิมล่ะ...

ถ้า .NET ถูกคงไม่มีปัญหาอะไร เดี๋ยวทำ Erlang ให้ผิดตามก็ได้

พอเข้าไปดู http://www.timestampgenerator.com/
แล้วลอง Generate ISO8601 ดู อ้าว Erlang ถูกนะ งั้น .NET framework (2.0) ก็ Bug น่ะสิ... เวรละ.... 

หรือเพราะ .NET ฉลาด!!! ดู Local time จาก Control Panel แล้วเทียบเวลา โดยไม่แปลง เวลาให้ลดลง 7 ชม. (ให้เป็น GMT) เพราะเข้าใจว่า มันคือ Local time zone

แล้วเวลาที่ได้มา 14:26:58 มันคือการบันทึกตอนไหน? ตอน 7:26:58 หรือ ตอน 14:26:58 

ต้องไปหาที่มาของเวลาแล้วค่อยกลับมาหาว่า ควรจะปรับใครยังไงอีกที

ไงละ แค่ DateTime :'(

วันอาทิตย์ที่ 19 มกราคม พ.ศ. 2557

Copy Code ข้ามเครื่อง แบบ Quick and Dirty

ช่วงนี้เป็นฤดู port code จาก Windows Service ที่เขียนด้วย .net มาเป็น Erlang แล้ว code ส่วนมากจะเป็น SQL Statement ที่เหมือนกัน ไม่ว่าจะเขียนด้วยภาษาอะไร พอโครงสร้างที่เขียนด้วย Erlang เสร็จ เช่นพวก Supervisor ไหนจะดูแล Process ไหน Process ไหนเป็น dynamic เป็น static ตรงไหนใช้ OTP บ้าง ครบหมด ถึงคิวต้อง port SQL Statement แล้ว

ซึ่งการ port (สำหรับผม) ไม่ใช่ว่า อ่าน code แล้วเขียนตามได้เลย อย่างน้อยๆ มันก็ code คนละประเภทกัน (OOP vs Functional) แต่สิ่งที่ต้อง port มาให้เหมือนๆ กันเลยก็คือ PL/SQL statement สำหรับ Oracle

ผมไม่ได้ดูแค่ตา เนื่องจากไม่ได้เขียน C# เลย ตอน Microsoft ออก C# ก็ปฏิเสธมาตลอด (อยู่แต่กับ C/C++) พอจะต้อง port C# ก็เลยต้องอาศัย Visual Studio (2005 อีกต่างหาก) debug ไป F9, F10, F11, Shift-F11 ไป เจอ code ก็ว่ากันไป

ทีนี้ถึงเวลาต้อง Copy SQL statement มาอีกเครื่อง เลยต้องหาทางที่สะดวกที่สุด เพราะ C# code ต้อง debug อยู่บน Windows7+Visual Studio 2005 ส่วน Erlang code อยู่บน Mac OSX

แต่ก่อน ส่ง message ระหว่างเครื่องผมใช้ MSN บ้าง Jabber บ้าง แต่ตอนนี้ไม่มี MSN ส่วน Jabber เสียเวลาสร้าง account อีก Google account ก็เป็น Hangouts หมด งง ใช้ไม่เป็น เสียเวลา ช้า

ค้นไปเจอ IP Messenger เข้า น่าสนใจครับ เล็ก เร็ว สะดวก ไม่ต้องมาสร้าง account วุ่นวาย Open Source ด้วย

บน Windows : http://www.ipmsg.org/index.html.en
บน OSX : http://ishwt.net/en/software/ipmsg/

ง่ายดี ไม่ต้องคิดมาก แถมมี code เผื่อเอาไปทำอะไรต่อยอดได้อีก