class//Ca210real

這一個就是真正操作CA-SDK的類別,並且將其函數通通重新詮釋成CA-210的操作行為。
裡面包含設定、量測、互動設計的部份。程式設計上,也有許多地方使用條件編譯,但是其實,在主程式的地方就將Ca210sim設定成debug版,Ca210real設定成release版。以免發佈時,還是模擬器的程式碼,量出一堆假的值。

CA-SDK本身,其實是使用Design Pattern的組合模式(Composite pattern)將CA-SDK做一個巧妙的設計,符合每一種可能的需求,而身為一個SDK應用開發,除了必須了解SDK的功能之外,還要了解如何將複雜的SDK包裝成適合自己需求的介面,降低複雜度,方便未來程式碼的維護和擴充速度。

Ca210real.h

#ifndef CA210REAL_H
#define CA210REAL_H

#include "CA210.h"
#include "ca200srvr.h"
#include "Bullet.h"

//單獨控制CA-210模組是不是要進入 DEBUG模式
//#define _CA210DEBUG _DEBUG

class Ca210real : public Ca210
{
    ICa200          *m_pICa200;
    ICa             *m_pICa;
    IProbe          *m_pIProbe;
    IMemory         *m_pIMemory;
    IProbeInfo      *m_pIProbeInfo;

    CaState m_caState;
    CaState m_caStateTemp;
    CString str;

    CString ImpsbStr;
    void MsgFrmt(CException* e, const CString&, const CString&);
    void MsgFrmt(const CString&);

public:
    Ca210real();
    virtual ~Ca210real();

public:
    const CaState CalZero();
    const CaState Measure();

    void LinkMemory();

    const MsrAiState MsrAI(const float& MsrDeviation = 0.645);//any constant value

    void SetOnline(const BOOL& b = TRUE);
    const BOOL isOnline() const
    { return m_caState == CA_Offline ? FALSE : TRUE; };

    const CString GetLcmSize();
    const CString GetChData();// const;
    const Bullet  GetMsrData();

//for setup Dialog Value
    const   float GetRangeColor1() const;
    const   float GetRangeColor2() const;
    const   float GetRangeFAM()    const;

             void SetChId(const LPCTSTR& Id)    { m_pIMemory->SetChannelID(Id); };
    const CString GetChId()               const {                               return m_pIMemory->GetChannelID(); };
    const CString GetChId(const long& No) const { m_pIMemory->SetChannelNO(No); return m_pIMemory->GetChannelID(); };

             void SetChNo(const long &No)  { m_pIMemory->SetChannelNO(No); };
    const    long GetChNo()          const { return m_pIMemory->GetChannelNO(); };
    const CString GetChStrNo() /*const*/   { str.Format("%ld", m_pIMemory->GetChannelNO()); return str; };

    const CString GetProb()       const { return m_pIProbe->GetSerialNO(); };
    const CString GetDeviceType() const { return m_pICa->GetCAType();      };
    const CString GetCaVersion()  const { return m_pICa->GetCAVersion();   };

    const CString GetRefProbe() const;
    const CString GetCalProbe() const;
    const CString GetCalMode()  const;

    const CString GetRefLv() const;
    const CString GetRefSx() const;
    const CString GetRefSy() const;

//for setup dialog combo box ItemString
    const CString GetSynMode      (const   SynMode&) const;
    const CString GetDisplayMode  (const   DisPlay&) const;
    const CString GetDisplayDigits(const DisDigits&) const;
    const CString GetAvgingMode   (const   AvgMode&) const;
    const CString GetBrigUnit     (const  BrigUnit&) const;
    const CString GetCalStandard  (const  CalStand&) const;

//Get and Set of CA-SDK 
    void SetSynMode      (const   SynMode&); const float GetSynMode()       const;
    void SetDisplayMode  (const   DisPlay&); const long  GetDisplayMode()   const;
    void SetDisplayDigits(const DisDigits&); const long  GetDisplayDigits() const;
    void SetAvgingMode   (const   AvgMode&); const long  GetAvgingMode()    const;
    void SetBrigUnit     (const  BrigUnit&); const long  GetBrigUnit()      const;
    void SetCalStandard  (const  CalStand&); const long  GetCalStandard()   const;
    
private:  //for setup CA-SDK parameter
    const float chooseSynMode      (const   SynMode&) const;
    const int   chooseDisplayMode  (const   DisPlay&) const;
    const int   chooseDisplayDigits(const DisDigits&) const;
    const int   chooseAvgingMode   (const   AvgMode&) const;
    const int   chooseBrigUnit     (const  BrigUnit&) const;
    const int   chooseCalStandard  (const  CalStand&) const;

private:  //for real CA-210 USB connect initial
    const BOOL initCreatCa200();
    const BOOL initConnectCa210();
    const BOOL initAttachCa();
    const BOOL initAttchProbe();
    
#ifdef _CA210DEBUG
    const CString GetSetupValue()             const;
             void debugMessageBox(const CString&) const;
#endif
};

#endif

Ca210real .cpp

初始化,初始化很多東西

CA-SDK的初始化,其實是要初始化不少東西。一步一步來,一步一步切開。為了讓出問題的那一步突顯出來,所以將try-catch給分開做,每一個Exception都會顯示自己所屬的函數,萬一出錯,還會告訴我是出錯在哪一步。在這一個地方,不會和IMemory連線,因為和它連線也是要花一段時間。連上IMemory的目的就只是要切換channel,這個部份就留在觸設定的地方再連線就可以了。
Ca210real::Ca210real():
ImpsbStr("1. 按「Prt Scm鍵」抓下目前的螢幕,並開小畫家貼上,另存成圖檔\n2. Mail給此程式設計者,詳細描述使用過程並將圖檔存成附件\n這是尚未預測到出現的問題。(應該不會發生的那種。)"),
m_pICa200(0), m_pICa(0), m_pIProbe(0), m_pIProbeInfo(0), m_pIMemory(0)
{
    DebugCode( DBugModeBox("TRUE of Ca210real()"); )

    if (initCreatCa200())
    if (initConnectCa210())
    if (initAttachCa())
    if (initAttchProbe())
        m_caState = CA_not_yet_ZeroCal;
}

const BOOL Ca210real::initCreatCa200()
{
    try
    {
        if (m_pICa200 == 0)            
            m_pICa200 = new ICa200();
        m_pICa200->CreateDispatch("CA200Srvr.Ca200.1");
        return TRUE;
    }
    catch (CException* e)
    {
        MsgFrmt(e, "CreatCa200();出問題", "1. 檢查USB線和量筒\n2. 按確定");        
        m_pICa200->ReleaseDispatch();
        return FALSE;
    }
}

const BOOL Ca210real::initConnectCa210()
{
    try
    {
        m_pICa200->m_bAutoRelease = TRUE;        
        m_pICa200->AutoConnect();
        return TRUE;
    }            
    catch (CException* e)
    {
        MsgFrmt(e, "LPDISPATCH pICa = m_pICa200->ConnectCa210();出問題", ImpsbStr);
        m_pICa200->ReleaseDispatch();
        return FALSE;
    }
}

const BOOL  Ca210real::initAttachCa()
{
    try
    {
        if (m_pICa == 0)
            m_pICa = new ICa();
        LPDISPATCH pICa(0);
        pICa = m_pICa200->GetSingleCa();
        m_pICa->AttachDispatch(pICa);
        return TRUE;
    }            
    catch (CException* e)
    {
        MsgFrmt(e, "LPDISPATCH pICa = m_pICa200->GetSingleCa();出問題", ImpsbStr);
        m_pICa200->ReleaseDispatch();
        return FALSE;
    }
}

const BOOL Ca210real::initAttchProbe()
{
    try
    {
        if (m_pIProbe     == 0) m_pIProbe     = new IProbe();
        if (m_pIProbeInfo == 0) m_pIProbeInfo = new IProbeInfo();

        LPDISPATCH pIProbe(0);
        pIProbe = m_pICa->GetSingleProbe();

        m_pIProbe->AttachDispatch(pIProbe);
        m_pIProbeInfo->AttachDispatch(pIProbe);
        return TRUE;
   }            
    catch (CException* e)
    {
        MsgFrmt(e, "LPDISPATCH pIProbe = m_pICa->GetSingleProbe();出問題", ImpsbStr);
        m_pICa->DetachDispatch();
        m_pICa200->ReleaseDispatch();
        return FALSE;
    }
}

Zero Cal

真實的CA-210是需要Zero Cal的,而....它總是會讓你忘記。一下要轉MEAS檔一下要轉0-Cal檔的。在此,程式上的設計會提醒你接下來該做什麼。
可以發現,設計上,會有一個迴圈將try-catch包起來,try裡只有m_pICa->CalZero();這個重點。
如果出錯了,就提醒你目前岀錯的地方是Probe沒有轉到0-Cal。
再執行一次,如果轉到0-Cal則會順利開始0-Cal,但是若你無視上一次的警告訊息,這次的警告訊息就會用簡短的話來「提醒」你。
畢竟剛準備量測時,還沒集中注意力,被程式發現,會改變語氣和你對話。這種被抓包的感覺,我想多多少少會提神一下。
const CaState Ca210real::CalZero()
{
    /*
    連線 實機
    caes 11   執行ZeroCal
                例外處理:
                  給MessageBox
                  再執行ZeroCal
    default   回傳FALSE  無法執行
    */
    if (isOnline())
    {
        DebugCode( DBugModeBox("TRUE of CalZero()"); )
        int flag(0);
        do 
        {
            try
            {
                m_pICa->CalZero();
                flag = 0;
            }
            catch (CException* e)
            {
                if (flag < 1)
                    MsgFrmt(e,"Probe還沒轉至0-Cal就執行Zero Cal", "1. 將量測筒轉至0-Cal檔。\n2. 按「確定」");
                else
                    MsgFrmt("1. 將量測筒轉至0-Cal檔。\n2. 按「確定」");
                SetOnline();
                flag++;
            }
        } while (flag);
        return m_caState = CA_ZeroCalMode;
    }
    else
    {
        DebugCode( DBugModeBox("CalZero() Offline."); )
        return m_caState = CA_Offline;
    }
}

取得量測值

和ZeroCal()一樣,用一個迴圈將try-catch包起來,不過,在此有一件特別的事要提醒,就是這一段程式碼,實現無形的Zero Cal,自然而然的完成了Zero Cal,你再也不需要記得,CA-210剛啟動時要Zero Cal。
m_pICa->Measure(0)如果出錯,有兩個原因

  1. 還沒ZeroCal
  2. 量測時疑似抓值太快造成出錯的不明原因
這兩個原因,我們都在catch處理。
如果沒有出錯,大多就是ZeroCal已完成,還要看一下,現在的Probe是不是還在0-Cal,還沒切換到MEAS。

catch的部份,先確定有連線再看看,前CA-SDK的狀態是否已經ZeroCal。
再各別處理之。

const CaState Ca210real::Measure()
{
    CaState Mode = m_caState;
    if(isOnline())
    {
        DebugCode(  DBugModeBox("TRUE of Measure()"); )
        int flag = 0;

        do 
        {
            try
            {
                m_pICa->Measure(0);

                //已經ZeroCal,檢查檔位是否正確
                if (m_pIProbe->GetLv() < 0.01)
                    Mode = CA_ZeroCalMode;     //is0-Cal檔
                else
                    Mode = CA_MsrMode;        //已ZeroCal, 檔位正確
                flag = 0;
            }
            catch (CException* e)
            {
                //MsrError
                SetOnline();
                if (m_caState == CA_not_yet_ZeroCal)  //還沒ZeroCal
                {
                    if (CalZero() == CA_Offline)
                        Mode = CA_Offline;
                    ++flag;
                }
                else
                {
                    TCHAR buf[255];
                    e->GetErrorMessage(buf, 255);
                    ++flag;
                    if (flag > 10)
                        //重覆太多次,導致類當機的情況。
                        break;
                    else
                        //在這不做任何動作,重覆執行迴圈內的東西。                      
                        continue;
                }
            }
        } while (flag);
    }
    return Mode;
}

自動抓值量測

為了互動上的設計,出現了一種自己判斷目前是否可以抓值的程式段,降低使用者使用時操作的IO設備以及降低操作人手。
const MsrAiState Ca210real::MsrAI(const float& MsrDeviation)
{
    DebugCode( DBugModeBox("TRUE of MsrAI(float MsrDeviation)"); )
        //第一筆資料暫存空間  //宣告誤差值計算空間
    
    float XFristValue = 0.0, deltaX = 0.0,
          YFristValue = 0.0, deltaY = 0.0,
          ZFristValue = 0.0, deltaZ = 0.0, deltaAll;
    
    if ( Measure() == CA_MsrMode )
    {
        //抓參考值
        XFristValue = m_pIProbe->GetX();
        YFristValue = m_pIProbe->GetY();
        ZFristValue = m_pIProbe->GetZ();
        
        if ( Measure() == CA_MsrMode )
        {
            //誤差取絕對值
            deltaX = ((XFristValue-m_pIProbe->GetX()) >= 0) ? XFristValue - m_pIProbe->GetX() : m_pIProbe->GetX() - XFristValue;
            deltaY = ((YFristValue-m_pIProbe->GetY()) >= 0) ? YFristValue - m_pIProbe->GetY() : m_pIProbe->GetY() - YFristValue;
            deltaZ = ((ZFristValue-m_pIProbe->GetZ()) >= 0) ? ZFristValue - m_pIProbe->GetZ() : m_pIProbe->GetZ() - ZFristValue;
        
            deltaAll = deltaX * deltaY * deltaZ;

            if (deltaAll < MsrDeviation )    return MA_InDeviation;//門檻值內
            else                             return MA_OutDeviation;//門檻值外
        }
        else
            return MA_nonMsr;
    }
    else
        return MA_nonMsr;  //無效量測,可能是沒連線之類的
}

讓程式知道目前量測的LCM是幾吋的

量測幾吋的LCM就要切換到對應的Channel,Channel的名稱都會記錄這是適用幾吋,將其字串取出,就可以少問使用者一個令人心煩意亂+腦袋空白的問題了。
const CString Ca210real::GetLcmSize()
{
    if(isOnline())
    {
        DebugCode( DBugModeBox("TRUE of GetLcmSize()"); )           
        try
        {
            if (m_LCMsize.IsEmpty())
                m_LCMsize.Format("%s", m_pIMemory->GetChannelID().Left(2));
        }
        catch (CException* e)
        {
            MsgFrmt(e, "模組尺寸大小設定出問題", ImpsbStr);
            m_LCMsize.Format("-1");
        }
    }
    else 
        m_LCMsize.Format("-1");

    return m_LCMsize;
}

設定CA-210的相關選項

重新將CA-SDK介面設計得和Setup CA的概念一樣。在CaSetupDlg時,程式碼將會更加精鍊。
const float Ca210real::GetRangeColor1() const
{
    float clr1, clr2;
    m_pICa->GetAnalogRange(&clr1, &clr2);
    return clr1;
}

const float Ca210real::GetRangeColor2() const
{
    float clr1, clr2;
    m_pICa->GetAnalogRange(&clr1, &clr2);
    return clr2;
}

const float Ca210real::GetRangeFAM() const
{
    float FMA;
    m_pICa->GetFMAAnalogRange(&FMA);
    return FMA;
}

const CString Ca210real::GetRefSx() const
{
    float Lv, Sx, Sy;
    m_pIMemory->GetReferenceColor(m_pIProbe->GetId(), &Sx, &Sy, &Lv);
    CString strSx;
    strSx.Format("%1.4f", Sx);
    return strSx;
}

const CString Ca210real::GetRefSy() const
{
    float Lv, Sx, Sy;
    m_pIMemory->GetReferenceColor(m_pIProbe->GetId(), &Sx, &Sy, &Lv);
    CString strSy;
    strSy.Format("%1.4f", Sy);
    return strSy;
}

const CString Ca210real::GetRefLv() const
{
    float Lv, Sx, Sy;
    m_pIMemory->GetReferenceColor(m_pIProbe->GetId(), &Sx, &Sy, &Lv);
    CString strLv;
    strLv.Format("%f", Lv);
    return strLv;
}

const CString Ca210real::GetRefProbe() const
{
    long CalProbe, RefProbe, CalMode;
    m_pIMemory->GetMemoryStatus(m_pIProbe->GetNumber(), &CalProbe, &RefProbe, &CalMode);
    CString str;
    str.Format("%ld", RefProbe);
    return str;
}

const CString Ca210real::GetCalProbe() const
{
    long CalProbe, RefProbe, CalMode;
    m_pIMemory->GetMemoryStatus(m_pIProbe->GetNumber(), &CalProbe, &RefProbe, &CalMode);
    CString str;
    str.Format("%ld", CalProbe);
    return str;
}

const CString Ca210real::GetCalMode() const
{
    long CalProbe, RefProbe, CalMode;
    m_pIMemory->GetMemoryStatus(m_pIProbe->GetNumber(), &CalProbe, &RefProbe, &CalMode);
    CString str;
    str.Format("%ld", CalMode);
    return str;
}
//////////////////////////////////////////////////////////////////////////

void Ca210real::SetSynMode(const SynMode& SmType)
{
    m_pICa->SetSyncMode(chooseSynMode(SmType));
}

const float Ca210real::chooseSynMode(const SynMode& SmType) const
{
    switch(SmType)
    {
    case SM_NTSC:  return  0.0;
    case SM_PAL:   return  1.0;
    case SM_EXT:   return  2.0;
    case SM_UNIV:  return  3.0;
    case SM_INT:   return  4.0;
    default:       return -1.0;
    }
}

const CString Ca210real::GetSynMode(const SynMode& SmType) const
{
    switch(SmType)
    {
    case SM_NTSC:   return "NTSC";
    case SM_PAL:    return "PAL";
    case SM_EXT:    return "EXT";
    case SM_UNIV:   return "UNIV";
    case SM_INT:    return "INT";
    default:        return "No this Mode";
    }
}

const float Ca210real::GetSynMode() const
{
    return m_pICa->GetSyncMode();
}
//////////////////////////////////////////////////////////////////////////
void Ca210real::SetDisplayMode(const DisPlay& DpType)
{
    m_pICa->SetDisplayMode(chooseDisplayMode(DpType));
}

const int Ca210real::chooseDisplayMode(const DisPlay& DpType) const
{
    return (int)DpType;
}

const CString Ca210real::GetDisplayMode(const DisPlay& DpType) const
{
    switch(DpType)
    {
    case DP_Lvxy:                return "xyLv";
    case DP_duvT:                return "duvTLv";    //return "duvT";
    case DP_Analyzer_NoDisplay:    return "RGB";        //return "Analyzer mode";
    case DP_Analyzer_Gstand:    return "RGB(G)";    //return "Analyzer mode(G)";
    case DP_Analyzer_Rstand:    return "RGB(R)";    //return "Analyzer mode(R)";
    case DP_dudv:               return "u'v'Lv'";    //return "u'v'";
    case DP_FMA:                return "FMA";
    case DP_XYZ:                return "XYZ";
    case DP_JEITA:              return "JEITA";
    default:                    return "No this Mode";
    }
}

const long Ca210real::GetDisplayMode() const
{
    return m_pICa->GetDisplayMode();
}
//////////////////////////////////////////////////////////////////////////

void Ca210real::SetDisplayDigits(const DisDigits& DdType)
{
    m_pICa->SetDisplayDigits(chooseDisplayDigits(DdType));
}

const int Ca210real::chooseDisplayDigits(const DisDigits& DdType) const
{
    switch(DdType)
    {
    case DD_3DisDigits: return 0;
    case DD_4DisDigits: return 1;
    default: return -1;
    }    
}

const CString Ca210real::GetDisplayDigits(const DisDigits& DdType) const
{
    switch(DdType)
    {
    case DD_3DisDigits: return "3";
    case DD_4DisDigits: return "4";
    default: return "Out of SPEC";
    }
}

const long Ca210real::GetDisplayDigits() const
{
    return m_pICa->GetDisplayDigits();
}
//////////////////////////////////////////////////////////////////////////
void Ca210real::SetAvgingMode(const AvgMode& AmType)
{
    m_pICa->SetAveragingMode(chooseAvgingMode(AmType));
}

const int Ca210real::chooseAvgingMode(const AvgMode& AmType) const
{
    switch(AmType)
    {
    case AM_SLOW: return 0;
    case AM_FAST: return 1;
    case AM_AUTO: return 2;
    default:      return -1;
    }    
}

const CString Ca210real::GetAvgingMode(const AvgMode& AmType) const
{
    switch(AmType)
    {
    case AM_SLOW: return "Slow";
    case AM_FAST: return "Fast";
    case AM_AUTO: return "Auto";
    default:      return "No this Mode";
    }
}

const long Ca210real::GetAvgingMode() const
{
    return m_pICa->GetAveragingMode();
}
//////////////////////////////////////////////////////////////////////////
void Ca210real::SetBrigUnit(const BrigUnit& BuType)
{
    m_pICa->SetBrightnessUnit(chooseBrigUnit(BuType));
}

const int Ca210real::chooseBrigUnit(const BrigUnit& BuType) const
{
    switch(BuType)
    {
    case BU_fL:   return 0;
    case BU_cdm2: return 1;
    default:      return -1;
    }    
}

const CString Ca210real::GetBrigUnit(const BrigUnit& BuType) const
{
    switch(BuType)
    {
    case BU_fL:   return "fL";
    case BU_cdm2: return "cd/m2";
    default:      return "No this Unit";
    }
}

const long Ca210real::GetBrigUnit() const
{
    return m_pICa->GetBrightnessUnit();
}

//////////////////////////////////////////////////////////////////////////
void Ca210real::SetCalStandard(const CalStand& CsType)
{
    m_pICa->SetCalStandard(chooseCalStandard(CsType));
}

const int Ca210real::chooseCalStandard(const CalStand& CsType) const
{
    switch(CsType)
    {
    case CS_6500K: return  1;
    case CS_9300K: return  2;
    default:       return -1;
    }
}

const CString Ca210real::GetCalStandard(const CalStand& CsType) const
{
    switch(CsType)
    {
    case CS_6500K: return "6500K";
    case CS_9300K: return "9300K";
    default:       return "Out of SPEC";
    }
}

const long Ca210real::GetCalStandard() const
{
    return m_pICa->GetCalStandard();
}

設定警告標示的標準格式

將錯誤訊息標準化的方式之一,就是將它做成專屬的函式或類別。在此使用相同的函式,使得CA-SDK複雜的錯誤訊息可以翻譯成讓使用者明白的文字訊息,只不過,有時候會有點囉嗦。
void Ca210real::MsgFrmt(CException* e, const CString& mean, const CString& steps)
{
    CString str;
    TCHAR buf[255];
    e->GetErrorMessage(buf, 255);
    
    str.Format("糟糕!!!出現ERROR!!!\n\n原廠錯誤訊息:\n%s\n\n看不懂沒關係,意思就是:\n「%s」\n\n你只要做:\n%s", buf, mean, steps);
    AfxMessageBox(str);
}

第二次的警告訊息

可以發現Measure和ZeroCal的功能中,都有一個迴圈將try-catch包起來,而第二次警告訊息總是精簡用字,因為發呆時被抓包,總是不會受到親切的訊息。
void Ca210real::MsgFrmt(const CString& steps)
{
    CString str;
    str.Format("你發呆嗎?\n%s", steps);
    AfxMessageBox(str);
}

沒有留言:

張貼留言

文章分類維度