基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版

本文详细介绍以CrossApp跨平台框架为基础,利用mosquito库和easySQLite库设计实现了基于MQTT协议的PC版步进电机控制客户端。

编译环境为VS2013,使用的语言主要是C++。

一、前期准备

本文所使用的跨平台界面库:
CrossApp官网:
http://crossapp.9miao.com/

CrossApp版本 1.5.4
下载地址:
https://github.com/babyliynfg/nano-CrossApp

本文使用的两个第三方库:
1、mosquitto,下载地址:
mosquitto-1.4.9
源码:http://www.eclipse.org/downloads/download.php?file=/mosquitto/source/mosquitto-1.4.9.tar.gz

2、easySQLite

官网地址:
https://code.google.com/archive/p/easysqlite/downloads

下载地址:
https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/easysqlite/easySQLite_v10.zip

本文将分三个部分来实现:
1、工程创建和界面的大致设计规划;
2、数据库的操作;
3、MQTT协议栈和功能实现。

以下是具体内容:

二、工程创建和界面的大致设计规划

解压之后:
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第1张图片

双击project-creator.exe:
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第2张图片
工程名填:StepMotorController
package Name那里中间填公司名,本项目随便填了一个MyCompany,最右边填App名字,本项目填StepMotorController
然后点击Create Project Now。
跳出一个对话框来,点确定,然后看到:
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第3张图片
然后关闭。
看到nano-CrossApp目录下出现一个projects文件夹,这个就是我们项目存放的目录,进去之后看到StepMotorController文件夹,里面就是我们的项目了。

我们先做Windows上的版本,然后再进一步兼容其他平台。

先把框架建起来:

然后双击nano-CrossApp\projects\StepMotorController\proj.win32下的StepMotorController.sln,打开vs工程,把StepMotorController设置为启动项,在debug版编译一下,运行调试,看到:
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第4张图片

参考CrossApp官方的test代码(在nano-CrossApp\samples\Test目录下),

从test项目下拷贝RootWindow.h、RootWindow.cpp、MenuViewController.h、MenuViewController.cpp

我们添加四个文件:StepMotorControlView.h、StepMotorControlView.cpp、SettingsViewController.h、SettingsViewController.cpp

具体内容如下:
RootWindow.h



#ifndef __HelloCpp__RootWindow__
#define __HelloCpp__RootWindow__

#include 
#include "CrossApp.h"

class RootWindow: public CAWindow, public CAKeypadDelegate
{

public:

    static RootWindow* getInstance();

    RootWindow();

    virtual ~RootWindow();

    virtual bool init();
    virtual void draw();
    CC_SYNTHESIZE_READONLY(CANavigationController*, m_pRootNavigationController, RootNavigationController);
    CC_SYNTHESIZE_READONLY(CADrawerController*, m_pRootDrawerController, DrawerController);

    void initUIView();

    virtual void keyBackClicked();
    void buttonCallBack(CAControl* btn,DPoint point);
};


#endif /* defined(__HelloCpp__ViewController__) */

RootWindow.cpp:



#include "RootWindow.h"
#include "MenuViewController.h"
#include "StepMotorControlView.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include 
#include "platform/android/jni/JniHelper.h"
#endif

static RootWindow* _window = NULL;

RootWindow* RootWindow::getInstance()
{
    if (_window == NULL)
    {
        _window = new RootWindow();
        _window->init();
        _window->autorelease();
    }
    return _window;

}

RootWindow::RootWindow()
:m_pRootNavigationController(NULL)
,m_pRootDrawerController(NULL)
{
    CAApplication::getApplication()->getKeypadDispatcher()->addDelegate(this);
}

RootWindow::~RootWindow()
{
    CAApplication::getApplication()->getKeypadDispatcher()->removeDelegate(this);
    if (m_pRootNavigationController)
        m_pRootNavigationController->release();
}

bool RootWindow::init()
{
    if (!CAWindow::init())
    {
        return false;
    }

    CAApplication::getApplication()->setNotificationView(CAView::createWithFrame(this->getBounds(), CAColor_green));

    this->initUIView();

    MenuViewController* _menuview = MenuViewController::create();

    CADrawerController* drawer = new CADrawerController();

    drawer->initWithController(_menuview, m_pRootNavigationController);
    drawer->setBackgroundImage(CAImage::create("image/bg.jpg"));
    drawer->setEffect3D(true);

    this->setRootViewController(drawer);
    drawer->autorelease();

    m_pRootDrawerController = drawer;
    CAApplication::getApplication()->setNotificationView(NULL);

    return true;
}

void RootWindow::draw()
{

}

void RootWindow::initUIView()
{
    do
    {
        CAViewController* viewController = m_pRootNavigationController ? m_pRootNavigationController->getViewControllerAtIndex(0) : NULL;

        CC_BREAK_IF(dynamic_cast(viewController));

        StepMotorControlView* tabBarController = new StepMotorControlView();
        tabBarController->init();
        tabBarController->autorelease();

        CANavigationBarItem* temp_nav = CANavigationBarItem::create(UTF8("控制器面板"));
        CABarButtonItem* item = CABarButtonItem::create("", CAImage::create("image/ic_category_list.png"), NULL);
        item->setTarget(this, CAControl_selector(RootWindow::buttonCallBack));
        temp_nav->addLeftButtonItem(item);
        tabBarController->setNavigationBarItem(temp_nav);


        if (m_pRootNavigationController)
        {
            m_pRootNavigationController->replaceViewController(tabBarController, false);
        }
        else
        {
            m_pRootNavigationController = new CANavigationController();
            m_pRootNavigationController->initWithRootViewController(tabBarController);
            m_pRootNavigationController->setNavigationBarBackgroundImage(CAImage::create("image/navbg.jpg"));
        }

    } while (0);

    if (m_pRootDrawerController)
    {
        m_pRootDrawerController->hideLeftViewController(true);
    }

    CAApplication::getApplication()->setStatusBarStyle(CAStatusBarStyleLightContent);
}

void RootWindow::buttonCallBack(CAControl* btn,DPoint point)
{
    this->getDrawerController()->showLeftViewController(true);
}


void RootWindow::keyBackClicked()
{
    CC_RETURN_IF(CAAlertView::hideWithDisplayed());

    if (this->getModalViewController())
    {
        this->dismissModalViewController(true);
    }
    else if (this->getDrawerController()->isShowLeftViewController())
    {
        this->getDrawerController()->hideLeftViewController(true);
    }
    else if (this->getRootNavigationController()->getViewControllerCount() > 1)
    {
        this->getRootNavigationController()->popViewControllerAnimated(true);
    }
    else
    {
        CAApplication::getApplication()->end();
    }
}

MenuViewController.h:

//
//  MenuViewController.h
//  Test
//
//  Created by renhongguang on 15/4/3.
//
//

#ifndef __Test__MenuViewController__
#define __Test__MenuViewController__

#include "RootWindow.h"

class MenuViewController : public CAViewController, CATableViewDelegate,CATableViewDataSource
{
public:

    MenuViewController();

    virtual ~MenuViewController();

    CREATE_FUNC(MenuViewController);

protected:

    void viewDidLoad();

    void viewDidUnload();

    void changeStatusBarOrientation(CAObject* obj);

public:
    virtual void tableViewDidSelectRowAtIndexPath(CATableView* table, unsigned int section, unsigned int row);

    virtual CATableViewCell* tableCellAtIndex(CATableView* table, const DSize& cellSize, unsigned int section, unsigned int row);
    virtual unsigned int numberOfRowsInSection(CATableView *table, unsigned int section);
    virtual unsigned int numberOfSections(CATableView *table);
    virtual unsigned int tableViewHeightForRowAtIndexPath(CATableView* table, unsigned int section, unsigned int row);
private:

    CATableView* tableView;

    CAImageView* m_pLogo;
};

#endif /* defined(__Test__MenuViewController__) */

MenuViewController.cpp:

//
//  MenuViewController.cpp
//  Test
//
//  Created by renhongguang on 15/4/3.
//
//

#include "MenuViewController.h"
#include "SettingsViewController.h"

MenuViewController::MenuViewController()
{
    CANotificationCenter::sharedNotificationCenter()->addObserver(this, callfuncO_selector(MenuViewController::changeStatusBarOrientation), CAApplicationDidChangeStatusBarOrientationNotification, NULL);
}

MenuViewController::~MenuViewController()
{
    CANotificationCenter::sharedNotificationCenter()->removeObserver(this, CAApplicationDidChangeStatusBarOrientationNotification);
}

void MenuViewController::viewDidLoad()
{
    this->getView()->setColor(CAColor_clear);

    DLayout tableViewLayout;
    DLayout logoLayout;
    const CAInterfaceOrientation& orientation = CAApplication::getApplication()->getStatusBarOrientation();
    if (orientation == CAInterfaceOrientationLandscape)
    {
        tableViewLayout = DLayout(DHorizontalLayoutFill, DVerticalLayout_B_H(0, 400));
        logoLayout = DLayout(DHorizontalLayout_W_C(261, 0.5), DVerticalLayout_T_H(120, 258));
    }
    else
    {
        tableViewLayout = DLayout(DHorizontalLayoutFill, DVerticalLayout_T_B(450, 0));
        logoLayout = DLayout(DHorizontalLayout_W_C(261, 0.5), DVerticalLayout_T_H(120, 258));
    }


    tableView = CATableView::createWithLayout(DLayoutFill);
    tableView->setLayout(tableViewLayout);
    tableView->setAllowsSelection(true);
    tableView->setTableViewDelegate(this);
    tableView->setTableViewDataSource(this);
    tableView->setBackgroundColor(CAColor_clear);
    tableView->setSeparatorColor(ccc4(166, 166, 166,100));
    tableView->setShowsScrollIndicators(false);
    tableView->setScrollEnabled(false);
    this->getView()->addSubview(tableView);

    m_pLogo = CAImageView::createWithImage(CAImage::create("image/logo.png"));
    m_pLogo->setLayout(logoLayout);
    this->getView()->addSubview(m_pLogo);

}

void MenuViewController::viewDidUnload()
{

}

void MenuViewController::changeStatusBarOrientation(CAObject* obj)
{
    const CAInterfaceOrientation& orientation = CAApplication::getApplication()->getStatusBarOrientation();

    DLayout tableViewLayout;
    DLayout logoLayout;
    if (orientation == CAInterfaceOrientationLandscape)
    {
        tableViewLayout = DLayout(DHorizontalLayoutFill, DVerticalLayout_B_H(0, 400));
        logoLayout = DLayout(DHorizontalLayout_W_C(261, 0.5), DVerticalLayout_T_H(120, 258));
    }
    else
    {
        tableViewLayout = DLayout(DHorizontalLayoutFill, DVerticalLayout_T_B(450, 0));
        logoLayout = DLayout(DHorizontalLayout_W_C(261, 0.5), DVerticalLayout_T_H(120, 258));
    }

    tableView->setLayout(tableViewLayout);
    m_pLogo->setLayout(logoLayout);
}

void MenuViewController::tableViewDidSelectRowAtIndexPath(CATableView* table, unsigned int section, unsigned int row)
{
    RootWindow::getInstance()->dismissModalViewController(true);
    if (row == 0)
    {
        RootWindow::getInstance()->initUIView();
    }
    else if (row == 1)
    {
        CAViewController* _settingsViewController = new SettingsViewController();
        _settingsViewController->init();
        _settingsViewController->setTitle(" ");

        _settingsViewController->autorelease();
        RootWindow::getInstance()->getDrawerController()->hideLeftViewController(true);
        RootWindow::getInstance()->getRootNavigationController()->pushViewController(_settingsViewController, true);

    }

}

#define _T(x) L##x
#define CHAR    wchar_t
static const CHAR* menuList[2] =
{
    _T("控制器面板"),  _T("服务器设置")//,_T("应用展示"), _T("CrossApp官网"),
};

CATableViewCell* MenuViewController::tableCellAtIndex(CATableView* table, const DSize& cellSize, unsigned int section, unsigned int row)
{
    CATableViewCell* cell = table->dequeueReusableCellWithIdentifier("CrossApp");
    if (cell == NULL)
    {
        cell = CATableViewCell::create("CrossApp");
        cell->setBackgroundView(NULL);
        CALabel* test = CALabel::createWithLayout(DLayout(DHorizontalLayout_L_R(50, 0), DVerticalLayoutFill));
        test->setTextAlignment(CATextAlignmentLeft);
        test->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
        test->setFontSize(32);
        test->setColor(CAColor_white);
        test->setTag(100);
        cell->addSubview(test);

        CAImageView* arrow = CAImageView::createWithLayout(DLayout(DHorizontalLayout_R_W(0, 64), DVerticalLayout_T_H(20, 64)));
        arrow->setTag(101);
        cell->addSubview(arrow);
    }
    CALabel* test = (CALabel*)cell->getSubviewByTag(100);
    test->setText(unicode_to_utf8(menuList[row]));// menuList[row]);
    CAImageView* arrow = (CAImageView*)cell->getSubviewByTag(101);
    arrow->setImage(CAImage::create("source_material/cell_btn_right.png"));

    return cell;
}

//菜单选项数目
unsigned int MenuViewController::numberOfRowsInSection(CATableView *table, unsigned int section)
{
    return 2;
}

unsigned int MenuViewController::numberOfSections(CATableView *table)
{
    return 1;
}

unsigned int MenuViewController::tableViewHeightForRowAtIndexPath(CATableView* table, unsigned int section, unsigned int row)
{
    return 100;
}

SettingsViewController.h:


#ifndef __SettingsViewController_h__
#define __SettingsViewController_h__

#include "CrossApp.h"


USING_NS_CC;

class SettingsViewController : public CAViewController,public CATextFieldDelegate
{
public:
    SettingsViewController();

    virtual ~SettingsViewController();

    void viewDidLoad();

    void viewDidUnload();

public:

    CAWebView* p_webView;

protected:
    virtual bool textFieldShouldBeginEditing(CATextField* sender);

    //If the sender doesn't want to detach from the IME, return true;
    virtual bool textFieldShouldEndEditing(CATextField* sender);

    //
    virtual void textFieldShouldReturn(CATextField* sender);

    virtual void keyBoardHeight(CATextField* sender, int height);

    //Warning!!! Warning!!! Warning!!!  This method is not on the OpenGL thread.
    virtual bool textFieldShouldChangeCharacters(CATextField* sender,
                                                 unsigned int location,
                                                 unsigned int lenght,
                                                 const std::string& changedText);

    void alertButtonCallBack(CAControl* btn,DPoint point);
private:

    CATextField* m_textField_IP;
    CATextField* m_textField_Port;
    CATextField* m_textField_Username;
    CATextField* m_textField_Password;
    CAButton* m_SaveBtn;
    std::string m_strIPInput;
    std::string m_strPortInput;

};

#endif /* defined(__SettingsViewController__) */

SettingsViewController.cpp:


#include "SettingsViewController.h"

SettingsViewController::SettingsViewController()
{

}

SettingsViewController::~SettingsViewController()
{
    this->getView()->removeSubview(p_webView);
    p_webView = NULL;
}

void SettingsViewController::viewDidLoad()
{
    CAImageView* view1 = CAImageView::createWithImage(CAImage::create("image/motor_control_view.jpg"));
    view1->setLayout(DLayoutFill);
    this->getView()->addSubview(view1);


    CALabel* label = CALabel::create();
    label->setColor(ccc4(51, 204, 255, 255));
    label->setText( UTF8( "网络参数设置"));
    label->setFontSize(36);
    label->setTextAlignment(CATextAlignmentLeft);
    label->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    label->setLayout(DLayout(DHorizontalLayout_W_C(240, 0.5), DVerticalLayout_H_C(40, 0.12)));
    this->getView()->addSubview(label);

    std::string ctn;
    m_textField_IP = CATextField::createWithLayout(DLayout(DHorizontalLayout_L_R(100, 100), DVerticalLayout_T_H(250, 100)));
    m_textField_IP->setTag(200);
    //PlaceHolder文本内容
    ctn = "IP: ";// +m_NetworkInfo.getIP();
    m_textField_IP->setPlaceHolderText(ctn);
    //键盘类型
    m_textField_IP->setKeyboardType(CATextField::Default);
    //TextField的对齐方式
    m_textField_IP->setTextFieldAlign(CATextField::Left);
    m_textField_IP->setDelegate(this);
    this->getView()->addSubview(m_textField_IP);

    m_textField_Port = CATextField::createWithLayout(DLayout(DHorizontalLayout_L_R(100, 100), DVerticalLayout_T_H(400, 100)));
    m_textField_Port->setTag(201);
    //PlaceHolder文本内容
    char str[256];
    sprintf(str,"Port:1883");
    ctn = std::string(str);
    m_textField_Port->setPlaceHolderText(ctn);
    //键盘类型
    m_textField_Port->setKeyboardType(CATextField::Default);
    //TextField的对齐方式
    m_textField_Port->setTextFieldAlign(CATextField::Left);
    m_textField_Port->setDelegate(this);
    this->getView()->addSubview(m_textField_Port);


    //初始化viewList
    m_SaveBtn = CAButton::create(CAButtonTypeRoundedRect);
    m_SaveBtn->setLayout(DLayout(DHorizontalLayout_W_C(240, 0.5), DVerticalLayout_H_C(54, 0.65)));
    m_SaveBtn->setTag(203);
    m_SaveBtn->setTitleFontSize(36);
    m_SaveBtn->setTitleForState(CAControlStateAll, UTF8("保存参数"));
    m_SaveBtn->addTarget(this, CAControl_selector(SettingsViewController::alertButtonCallBack), CAControlEventTouchUpInSide);
    this->getView()->addSubview(m_SaveBtn);

}

void SettingsViewController::viewDidUnload()
{

}

bool SettingsViewController::textFieldShouldBeginEditing(CATextField* sender)
{
    return true;
}

//If the sender doesn't want to detach from the IME, return true;
bool SettingsViewController::textFieldShouldEndEditing(CATextField* sender)
{
    if(sender == m_textField_IP)
    {
        m_strIPInput = sender->getText();
    }
    else if(sender == m_textField_Port)
    {
        m_strPortInput = sender->getText();
    }
    else if(sender == m_textField_Username)
    {

    }
    else if(sender == m_textField_Password)
    {

    }
    return true;
}


//
void SettingsViewController::textFieldShouldReturn(CATextField* sender)
{
}


void SettingsViewController::keyBoardHeight(CATextField* sender, int height)
{
}


//Warning!!! Warning!!! Warning!!!  This method is not on the OpenGL thread.
bool SettingsViewController::textFieldShouldChangeCharacters(CATextField* sender,
                                             unsigned int location,
                                             unsigned int lenght,
                                                    const std::string& changedText)
{
    return true;
}

void SettingsViewController::alertButtonCallBack(CAControl* btn,DPoint point)
{
    int tag = btn->getTag();
    switch (tag) {
        case 203:
        {
            CAAlertView* alertView = CAAlertView::createWithText(UTF8( "提示"),UTF8(  "保存成功!"),UTF8( "关闭"),NULL);
            alertView->show();
            break;
        }            
        default:
            break;
    }
}

StepMotorControlView.h:


#ifndef __StepMotorControlView_h__
#define __StepMotorControlView_h__

#include 
#include "CrossApp.h"
#include "CrossAppExt.h"


USING_NS_CC;

class StepMotorControlView : public CAViewController
{

public:

    StepMotorControlView();

    virtual ~StepMotorControlView();

protected:

    void viewDidLoad();

    void viewDidUnload();
private:
    void SliderValueChange(CAControl* btn, DPoint point);
    CALabel* m_VelocityValue;
    CASlider* m_Slider;  

    void switchStateChange(CAControl* btn, DPoint point);
    CASwitch* m_RunSwitch;
    CASwitch* m_DirectionSwitch;

public:
    void SendFCSMessage(const std::string& strFCSMsg);//FilmCenterSettings message
    void SendMessage(const char *pszFormat,...);
    void SendDirMessage(int idx, int direction);
    void SendLoopMessage(int idx, int loop);
    void SendRecMessage(int idx, int recording);
    void SendStepsMessage(int idx, int recording);
    void SendActionMessage(int idx, int action,int direction,int velocity);
    void SendVelocityMessage(int idx, int velocity);
private:

};


#endif /* defined(__HelloCpp__ViewController__) */

StepMotorControlView.cpp:


#include "StepMotorControlView.h"
#include "platform/CACommon.h"

#define FontColor ccc4(51,204,255,255) //ccc4(255,255,255,255)


StepMotorControlView::StepMotorControlView()
    :m_Slider(NULL)
{

}

StepMotorControlView::~StepMotorControlView()
{
    CADrawerController* drawer = (CADrawerController*)CAApplication::getApplication()->getRootWindow()->getRootViewController();
    drawer->setTouchMoved(true);
}

void StepMotorControlView::viewDidLoad()
{

    CAImageView* view1 = CAImageView::createWithImage(CAImage::create("image/motor_control_view.jpg"));
    view1->setLayout(DLayoutFill);
    this->getView()->addSubview(view1);


    int var=0;

    CALabel* labelDirectionSwitch = CALabel::createWithLayout(DLayout(DHorizontalLayout_L_W(10, 200), DVerticalLayout_T_H(100, 40)));
    labelDirectionSwitch->setColor(FontColor);
    labelDirectionSwitch->setText(UTF8("方向开关"));
    labelDirectionSwitch->setFontSize(30);
    labelDirectionSwitch->setTextAlignment(CATextAlignmentCenter);
    labelDirectionSwitch->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    this->getView()->addSubview(labelDirectionSwitch);

    m_DirectionSwitch =  CASwitch::createWithLayout(DLayout(DHorizontalLayout_L_W(250, 100), DVerticalLayout_T_H(100, 20)));
    m_DirectionSwitch->setTag(102);

    m_DirectionSwitch->setIsOn(var==0?false:true, false);
    m_DirectionSwitch->addTarget(this, CAControl_selector(StepMotorControlView::switchStateChange));
    this->getView()->addSubview(m_DirectionSwitch);


    //CAView* view1 = CAView::createWithLayout(DLayoutFill);
    //view1->setColor(CAColor_gray);

    m_VelocityValue = CALabel::createWithLayout(DLayout(DHorizontalLayout_L_R(100, 100), DVerticalLayout_T_H(200, 50)));
    m_VelocityValue->setColor(FontColor);
    char str[64];
    sprintf(str,"%d%%",0x0);//m_StepMotorHardware.getVelocity()
    m_VelocityValue->setText(str);
    m_VelocityValue->setFontSize(30);
    m_VelocityValue->setTextAlignment(CATextAlignmentCenter);
    m_VelocityValue->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    this->getView()->addSubview(m_VelocityValue);

    m_Slider = CASlider::createWithLayout(DLayout(DHorizontalLayout_L_R(120, 120), DVerticalLayout_T_H(250, 56)));
    m_Slider->addTarget(this, CAControl_selector(StepMotorControlView::SliderValueChange));
    m_Slider->setTag(100);
    m_Slider->setValue(0);
    this->getView()->addSubview(m_Slider);

    CALabel* labelStepMotorSwitch = CALabel::createWithLayout(DLayout(DHorizontalLayout_L_W(10, 200), DVerticalLayout_T_H(400, 40)));
    labelStepMotorSwitch->setColor(FontColor);
    labelStepMotorSwitch->setText(UTF8("电机开关"));
    labelStepMotorSwitch->setFontSize(30);
    labelStepMotorSwitch->setTextAlignment(CATextAlignmentCenter);
    labelStepMotorSwitch->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    this->getView()->addSubview(labelStepMotorSwitch);

    m_RunSwitch = CASwitch::createWithLayout(DLayout(DHorizontalLayout_L_W(250, 100), DVerticalLayout_T_H(400, 20)));
    m_RunSwitch->setTag(101);
    var = 0;
    m_RunSwitch->setIsOn(var == 0 ? false : true, false);
    m_RunSwitch->addTarget(this, CAControl_selector(StepMotorControlView::switchStateChange));
    this->getView()->addSubview(m_RunSwitch);

}

void StepMotorControlView::viewDidUnload()
{
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;

}

void StepMotorControlView::SliderValueChange(CAControl* btn, DPoint point)
{
    char value[20] = "";
    CASlider* p_Slider = (CASlider*)btn;
    sprintf(value, "%.02f%%", p_Slider->getValue() * 100);
    if (p_Slider->getTag()==100) {
        m_VelocityValue->setText(value);
        static int old_val = (int)(p_Slider->getValue() * 100);
        if(old_val != (int)(p_Slider->getValue() * 100))
        {
            old_val = (int)(p_Slider->getValue() * 100);
        }

    }

}

void StepMotorControlView::switchStateChange(CAControl* btn, DPoint point)
{
    CASwitch* state = (CASwitch*)btn;


    switch(state->getTag())
    {
    case 101://action

            break;
    case 102://direction

            break;
    default:
        break;

    }

}

void StepMotorControlView::SendFCSMessage(const std::string& strFCSMsg)
{
    CCLog("not implement");
}

#define MAX_TEXT 256
void StepMotorControlView::SendMessage(const char *pszFormat,...)
{
    char msg[256]={0};
    va_list ap;
    va_start(ap, pszFormat);
#if defined(_WIN32 )
    vsnprintf_s(msg, MAX_TEXT, MAX_TEXT, pszFormat, ap);
#else //#elif defined(__GNUC__)
    vsnprintf(msg, MAX_TEXT, pszFormat, ap);
#endif
    va_end(ap);
    SendFCSMessage(msg);
}

void StepMotorControlView::SendDirMessage(int idx, int direction)
{
    SendMessage("{\nidx:%d,\ndir:%d\n}",idx,direction);
}

void StepMotorControlView::SendLoopMessage(int idx, int loop)
{
    SendMessage("{\nidx:%d,\nloop:%d\n}",idx,loop);
}
void StepMotorControlView::SendRecMessage(int idx, int recording)
{
    SendMessage("{\nidx:%d,\nrecording:%d\n}",idx,recording);
}
void StepMotorControlView::SendStepsMessage(int idx, int steps)
{
    SendMessage("{\nidx:%d,\nsteps:%d\n}",idx,steps);
}

void StepMotorControlView::SendActionMessage(int idx, int action, int direction, int velocity)
{
    SendMessage("{\nidx:%d,\nact:%d,\ndir:%d,\nv:%d\n}",idx,action,direction,velocity);
}

void StepMotorControlView::SendVelocityMessage(int idx, int velocity)
{
    SendMessage("{\nidx:%d,\nv:%d\n}",idx,velocity);
}

把nano-CrossApp\samples\Test下的Resources复制到nano-CrossApp\projects\StepMotorController下。

再找几个1080x1920的图片放到nano-CrossApp\projects\StepMotorController\Resources\image下,作为背景图片。

初步大概的界面效果是:

基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第5张图片

基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第6张图片

基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第7张图片

三、数据库的操作

接下来,把easySQLite移植过来,实现对参数的存取。

把easySQLite_v10.zip\easySQLite\easySQLite下的整个easySQLite拷贝到nano-CrossApp\projects\StepMotorController\Classes目录下。
在vs上的StepMotorCotroller项目的Classes下新建一个筛选器,命名为easySQLite。并把刚才拷贝过来的文件加入进来。
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第8张图片
F7编译一把,没问题。
其实这里有个问题,在安卓版编译时比较严重,那就是CrossApp框架本身已经集成了sqlite3这个模块进去了,链接会错误说重复定义。vs版没错误。

写三个个辅助类,简化对数据库的操作:
NetworkINFO类,对服务器的参数设置进行数据库操作。
EquipmentINFO类,对步进电机设备参数进行数据库操作。
SettingsHelper类,创建、打开或关闭数据库。

这部分放在SettingsHelper模块中:

SettingsHelper.h:

#ifndef __SETTINGS_HELPER_H__
#define __SETTINGS_HELPER_H__

#include 
#include "easySQLite/SqlTable.h"


class NetworkINFO
{
public:
    NetworkINFO();

    std::string getIP();
    void setIP(const std::string& ip);

    int getPort();
    void setPort(int port);

    std::string getUsername();
    void setUsername(const std::string& username);

    std::string getPassword();
    void setPasswork(const std::string& password);

    bool SaveToDatabase();
    bool ReadFromDatabase();

private:
    std::string m_MQTTIP;
    int m_MQTTIPPort;

    std::string m_MQTTUsername;
    std::string m_MQTTPassword;

    sql::Table m_tbNetworkSettings;
};

class SlideRailINFO
{
public:
    int getRate();
    void setRate(int rate);

};


class EquipmentINFO
{
public:
    explicit EquipmentINFO(const std::string& name);

    int getIDX();
    void setIDX(int idx); 

    int getActionState();
    void setActionSate(int state);

    int getDirection();
    void setDirection(int direction);

    int getSteps();
    void setSteps(int steps);

    int getVelocity();
    void setVelocity(int velocity);

    int getLoopFlag();
    void setLoopFlag(int flag);

    int getRecordingFlag();
    void setRecordingFlag(int flag);

    bool SaveToDatabase();
    bool ReadFromDatabase();

protected:
    int m_IDX;//设备索引号
    int m_ActionState;//执行状态
    int m_Direction;//方向
    int m_Steps;//要转的步数
    int m_Velocity;//速度
    int m_LoopFlag;//是否循环标志位
    int m_RecordingFlag;//是否录制过程标志位
    sql::Table m_tbEquipments;

};


class SettingsHelper
{
public:
    static SettingsHelper* getInstance();
    ~SettingsHelper();
private:
    SettingsHelper();
    bool OpenDatabase();
    void CloseDatabase();
    static SettingsHelper* instance_;
};

#endif
#include "SettingsHelper.h"

#include "easySQLite/SqlCommon.h"
#include "easySQLite/SqlField.h"
#include "easySQLite/SqlDatabase.h"
#include "easySQLite/SqlTable.h"
#include "easySQLite/SqlValue.h"
#include "CrossApp.h"

static sql::Database m_db;

using namespace sql;


Field definition_tbNetworkSettings[] =
{
    Field(FIELD_KEY),
    Field("ip", type_text, flag_not_null),
    Field("port", type_int, flag_not_null),
    Field("username", type_text),
    Field("password", type_text),
    Field(DEFINITION_END),
};

NetworkINFO::NetworkINFO()
    :m_tbNetworkSettings(m_db.getHandle(), "NetworkSettings", definition_tbNetworkSettings)
{
    if (!m_tbNetworkSettings.exists())
    {
        m_tbNetworkSettings.create();

        Record record(m_tbNetworkSettings.fields());

        record.setString("ip", "127.0.0.1");
        record.setInteger("port", 1883);
        record.setNull("username");
        record.setNull("password");
        if (!m_tbNetworkSettings.addRecord(&record))
        {
            CCLog("NetworkINFO ERR: addRecord");
        }
    }

    ReadFromDatabase();
}

std::string NetworkINFO::getIP()
{
    return m_MQTTIP;
}

void NetworkINFO::setIP(const std::string& ip)
{
    m_MQTTIP = ip;
}

int NetworkINFO::getPort()
{
    return m_MQTTIPPort;
}

void NetworkINFO::setPort(int port)
{
    m_MQTTIPPort = port;
}


std::string NetworkINFO::getUsername()
{
    return m_MQTTUsername;
}

void NetworkINFO::setUsername(const std::string& username)
{
    m_MQTTUsername = username;
}


std::string NetworkINFO::getPassword()
{
    return m_MQTTPassword;
}

void NetworkINFO::setPasswork(const std::string& password)
{
    m_MQTTPassword = password;
}

bool NetworkINFO::SaveToDatabase()
{
    m_tbNetworkSettings.open();
    if (Record* record = m_tbNetworkSettings.getRecord(0))
    {

        record->setString("ip", m_MQTTIP);
        record->setInteger("port", m_MQTTIPPort);
        record->setString("username",m_MQTTUsername);
        record->setString("password",m_MQTTPassword);

        return m_tbNetworkSettings.updateRecord(record);
    }
    else
    {
        return false;
    }

}

bool NetworkINFO::ReadFromDatabase()
{
    m_tbNetworkSettings.open();
    //int count = m_tbNetworkSettings.recordCount();
    Record* record = m_tbNetworkSettings.getRecord(0);
    if (record)
    {
        CCLog(record->toString().c_str());
        Value* v= record->getValue("ip");
        if (v)
        {
            //CCLog(v->asString().c_str());
            m_MQTTIP = v->asString();

        }
        v = record->getValue("port");
        if (v)
        {
            m_MQTTIPPort = v->asInteger();
        }

        v= record->getValue("username");
        if (v)
        {
            //CCLog(v->asString().c_str());
            m_MQTTUsername = v->asString();

        }
        v = record->getValue("password");
        if (v)
        {
            m_MQTTPassword = v->asString();
        }
        return true;
    }
    else
    {
        return false;
    }

}


//--------------------------------------------------------
/*
    const std::string m_Name;
    int m_IDX;
    int m_ActionState;
    int m_Direction;
    int m_Steps;
    int m_Velocity;
    int m_LoopFlag;
    int m_RecordingFlag;
*/

Field definition_tbEquipments[] =
{
    Field(FIELD_KEY),
    Field("name", type_text, flag_not_null),
    Field("idx", type_int, flag_not_null),
    Field("actionsate", type_int, flag_not_null),
    Field("direction", type_int, flag_not_null),
    Field("steps", type_int, flag_not_null),
    Field("velocity", type_int, flag_not_null),
    Field("loopflag", type_int, flag_not_null),
    Field("recordingflag", type_int, flag_not_null),
    Field(DEFINITION_END),
};

EquipmentINFO::EquipmentINFO(const std::string& name)
    :m_tbEquipments(m_db.getHandle(), name, definition_tbEquipments)
{
    if (!m_tbEquipments.exists())
    {
        m_tbEquipments.create();

        Record record(m_tbEquipments.fields());

        record.setString("name", name);
        record.setInteger("idx", 0);
        record.setInteger("actionsate", 0);
        record.setInteger("direction", 0);
        record.setInteger("steps", 0);
        record.setInteger("velocity", 0);
        record.setInteger("loopflag", 0);
        record.setInteger("recordingflag", 0);
        if (!m_tbEquipments.addRecord(&record))
        {
            CCLog("EquipmentINFO ERR: addRecord");
        }
    }

    ReadFromDatabase();
}

int EquipmentINFO::getIDX()
{
    return m_IDX;
}
void EquipmentINFO::setIDX(int idx)
{
    m_IDX = idx>=0?idx:0;
} 

int EquipmentINFO::getActionState()
{
    return m_ActionState;
}
void EquipmentINFO::setActionSate(int state)
{
    m_ActionState = state>0?1:0;
}

int EquipmentINFO::getDirection()
{
    return m_Direction;
}
void EquipmentINFO::setDirection(int direction)
{
    m_Direction = direction>0?1:0;
}

int EquipmentINFO::getSteps()
{
    return m_Steps;
}
void EquipmentINFO::setSteps(int steps)
{
    m_Steps = steps>0?steps:0;
}

int EquipmentINFO::getVelocity()
{
    return m_Velocity;
}
void EquipmentINFO::setVelocity(int velocity)
{
    m_Velocity = velocity>0?velocity:0;
}

int EquipmentINFO::getLoopFlag()
{
    return m_LoopFlag;
}
void EquipmentINFO::setLoopFlag(int flag)
{
    m_LoopFlag = flag>0?1:0;
}
int EquipmentINFO::getRecordingFlag()
{
    return m_RecordingFlag;
}
void EquipmentINFO::setRecordingFlag(int flag)
{
    m_RecordingFlag = flag>0?1:0;
}

bool EquipmentINFO::SaveToDatabase()
{
    m_tbEquipments.open();
    if (Record* record = m_tbEquipments.getRecord(0))
    {
        record->setInteger("idx", m_IDX);
        record->setInteger("actionsate", m_ActionState);
        record->setInteger("direction", m_Direction);
        record->setInteger("steps", m_Steps);
        record->setInteger("velocity", m_Velocity);
        record->setInteger("loopflag", m_LoopFlag);
        record->setInteger("recordingflag", m_RecordingFlag);

        return m_tbEquipments.updateRecord(record);
    }
    else
    {
        return false;
    }
}

bool EquipmentINFO::ReadFromDatabase()
{
    m_tbEquipments.open();
    //int count = m_tbNetworkSettings.recordCount();
    Record* record = m_tbEquipments.getRecord(0);
    if (record)
    {
        CCLog(record->toString().c_str());
        Value* v= record->getValue("idx");
        if (v)
        {
            m_IDX = v->asInteger();

        }
        v= record->getValue("actionsate");
        if (v)
        {
            m_ActionState = v->asInteger();

        }
        v= record->getValue("direction");
        if (v)
        {
            m_Direction = v->asInteger();

        }
        v= record->getValue("steps");
        if (v)
        {
            m_Steps = v->asInteger();

        }
        v= record->getValue("velocity");
        if (v)
        {
            m_Velocity = v->asInteger();

        }
        v= record->getValue("loopflag");
        if (v)
        {
            m_LoopFlag = v->asInteger();

        }
        v= record->getValue("recordingflag");
        if (v)
        {
            m_RecordingFlag = v->asInteger();

        }

        return true;
    }
    else
    {
        return false;
    }
}

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


SettingsHelper* SettingsHelper::instance_ = NULL;

SettingsHelper* SettingsHelper::getInstance()
{
    if(instance_== NULL)
    {
        instance_ = new SettingsHelper();
    }
    return instance_;
}

SettingsHelper::SettingsHelper()
{
    OpenDatabase(); 
}

SettingsHelper::~SettingsHelper()
{
    CloseDatabase();
}

 bool SettingsHelper::OpenDatabase() 
 { 
    std::string path= CCFileUtils::sharedFileUtils()->getWritablePath()+"Equipments.db";
    try
    {
        m_db.open(path);
        //...

    } catch (Exception e) {
        //...
        CCLog("open database failed:[ %s ]",e.msg().c_str());
    }
     return true; 
 }

 void SettingsHelper::CloseDatabase()
 {
    m_db.close();
 }

修改SettingsViewCotorller.h:


#ifndef __SettingsViewController_h__
#define __SettingsViewController_h__

#include "CrossApp.h"
#include "SettingsHelper.h"

USING_NS_CC;

class SettingsViewController : public CAViewController,public CATextFieldDelegate
{
public:
    SettingsViewController();

    virtual ~SettingsViewController();

    void viewDidLoad();

    void viewDidUnload();

public:

    CAWebView* p_webView;

protected:
    virtual bool textFieldShouldBeginEditing(CATextField* sender);

    //If the sender doesn't want to detach from the IME, return true;
    virtual bool textFieldShouldEndEditing(CATextField* sender);

    //
    virtual void textFieldShouldReturn(CATextField* sender);

    virtual void keyBoardHeight(CATextField* sender, int height);

    //Warning!!! Warning!!! Warning!!!  This method is not on the OpenGL thread.
    virtual bool textFieldShouldChangeCharacters(CATextField* sender,
                                                 unsigned int location,
                                                 unsigned int lenght,
                                                 const std::string& changedText);

    void alertButtonCallBack(CAControl* btn,DPoint point);
private:
    NetworkINFO m_NetworkInfo;
    CATextField* m_textField_IP;
    CATextField* m_textField_Port;
    CATextField* m_textField_Username;
    CATextField* m_textField_Password;
    CAButton* m_SaveBtn;
    std::string m_strIPInput;
    std::string m_strPortInput;

};

#endif /* defined(__SettingsViewController__) */

SettingsViewController.cpp修改如下:


#include "SettingsViewController.h"

SettingsViewController::SettingsViewController()
{
    CCLog(m_NetworkInfo.getIP().c_str());
    CCLog("%d", m_NetworkInfo.getPort());
    CCLog(m_NetworkInfo.getUsername().c_str());
    CCLog(m_NetworkInfo.getPassword().c_str());
}

SettingsViewController::~SettingsViewController()
{
    this->getView()->removeSubview(p_webView);
    p_webView = NULL;
}

void SettingsViewController::viewDidLoad()
{
    CAImageView* view1 = CAImageView::createWithImage(CAImage::create("image/motor_control_view.jpg"));
    view1->setLayout(DLayoutFill);
    this->getView()->addSubview(view1);


    CALabel* label = CALabel::create();
    label->setColor(ccc4(51, 204, 255, 255));
    label->setText( UTF8( "网络参数设置"));
    label->setFontSize(36);
    label->setTextAlignment(CATextAlignmentLeft);
    label->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    label->setLayout(DLayout(DHorizontalLayout_W_C(240, 0.5), DVerticalLayout_H_C(40, 0.12)));
    this->getView()->addSubview(label);

    std::string ctn;
    m_textField_IP = CATextField::createWithLayout(DLayout(DHorizontalLayout_L_R(100, 100), DVerticalLayout_T_H(250, 100)));
    m_textField_IP->setTag(200);
    //PlaceHolder文本内容
    ctn = "IP: " +m_NetworkInfo.getIP();
    m_textField_IP->setPlaceHolderText(ctn);
    //键盘类型
    m_textField_IP->setKeyboardType(CATextField::Default);
    //TextField的对齐方式
    m_textField_IP->setTextFieldAlign(CATextField::Left);
    m_textField_IP->setDelegate(this);
    this->getView()->addSubview(m_textField_IP);

    m_textField_Port = CATextField::createWithLayout(DLayout(DHorizontalLayout_L_R(100, 100), DVerticalLayout_T_H(400, 100)));
    m_textField_Port->setTag(201);
    //PlaceHolder文本内容
    char str[256];
    sprintf(str, "Port: %d", m_NetworkInfo.getPort());
    ctn = std::string(str);
    m_textField_Port->setPlaceHolderText(ctn);
    //键盘类型
    m_textField_Port->setKeyboardType(CATextField::Default);
    //TextField的对齐方式
    m_textField_Port->setTextFieldAlign(CATextField::Left);
    m_textField_Port->setDelegate(this);
    this->getView()->addSubview(m_textField_Port);


    //初始化viewList
    m_SaveBtn = CAButton::create(CAButtonTypeRoundedRect);
    m_SaveBtn->setLayout(DLayout(DHorizontalLayout_W_C(240, 0.5), DVerticalLayout_H_C(54, 0.65)));
    m_SaveBtn->setTag(203);
    m_SaveBtn->setTitleFontSize(36);
    m_SaveBtn->setTitleForState(CAControlStateAll, UTF8("保存参数"));
    m_SaveBtn->addTarget(this, CAControl_selector(SettingsViewController::alertButtonCallBack), CAControlEventTouchUpInSide);
    this->getView()->addSubview(m_SaveBtn);

}

void SettingsViewController::viewDidUnload()
{

}

bool SettingsViewController::textFieldShouldBeginEditing(CATextField* sender)
{
    return true;
}

//If the sender doesn't want to detach from the IME, return true;
bool SettingsViewController::textFieldShouldEndEditing(CATextField* sender)
{
    if(sender == m_textField_IP)
    {
        m_strIPInput = sender->getText();
    }
    else if(sender == m_textField_Port)
    {
        m_strPortInput = sender->getText();
    }
    else if(sender == m_textField_Username)
    {

    }
    else if(sender == m_textField_Password)
    {

    }
    return true;
}


//
void SettingsViewController::textFieldShouldReturn(CATextField* sender)
{
}


void SettingsViewController::keyBoardHeight(CATextField* sender, int height)
{
}


//Warning!!! Warning!!! Warning!!!  This method is not on the OpenGL thread.
bool SettingsViewController::textFieldShouldChangeCharacters(CATextField* sender,
                                             unsigned int location,
                                             unsigned int lenght,
                                                    const std::string& changedText)
{
    return true;
}

void SettingsViewController::alertButtonCallBack(CAControl* btn,DPoint point)
{
    int tag = btn->getTag();
    switch (tag) {
        case 203:
        {
            if (!m_strIPInput.empty())
                m_NetworkInfo.setIP(m_strIPInput);
            if (!m_strPortInput.empty())
                m_NetworkInfo.setPort(atoi(m_strPortInput.c_str()));
            m_NetworkInfo.SaveToDatabase();
            CAAlertView* alertView = CAAlertView::createWithText(UTF8("提示"), UTF8("保存成功!"), UTF8("关闭"), NULL);
            alertView->show();
            break;
        }            
        default:
            break;
    }
}

接下来,要为SettingsHelper类实例化。
在AppDelegate.h中加入

#include "SettingsHelper.h"

在类AppDelegate中加入成员数据:

SettingsHelper *m_SettingsHelper;

在AppDelegate的构造函数中加入对其赋值:

AppDelegate::AppDelegate()
{
    m_SettingsHelper = SettingsHelper::getInstance();//added @2017.10.01
}

编译运行,看看效果:
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第9张图片

在 bool SettingsHelper::OpenDatabase() 中打上断点,重新运行到断点,可以看到:

基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第10张图片

这是我们的数据库存放的具体位置,手动找到这个数据库如下:

基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第11张图片

修改一下界面中的参数、保存,验证一下功能:
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第12张图片

退出后重进,可以看到前面保存的参数已经读出来了。

然后在StepMotorControlView类中加入成员变量:

    EquipmentINFO m_StepMotorHardware;

然后子修改StepMotorControlView.cpp:


#include "StepMotorControlView.h"
#include "platform/CACommon.h"

#define FontColor ccc4(51,204,255,255) //ccc4(255,255,255,255)


StepMotorControlView::StepMotorControlView()
    :m_Slider(NULL), m_StepMotorHardware("mainstepmotor")
{

}

StepMotorControlView::~StepMotorControlView()
{
    CADrawerController* drawer = (CADrawerController*)CAApplication::getApplication()->getRootWindow()->getRootViewController();
    drawer->setTouchMoved(true);
    m_StepMotorHardware.SaveToDatabase();
}

void StepMotorControlView::viewDidLoad()
{

    CAImageView* view1 = CAImageView::createWithImage(CAImage::create("image/motor_control_view.jpg"));
    view1->setLayout(DLayoutFill);
    this->getView()->addSubview(view1);


    int var=0;

    CALabel* labelDirectionSwitch = CALabel::createWithLayout(DLayout(DHorizontalLayout_L_W(10, 200), DVerticalLayout_T_H(100, 40)));
    labelDirectionSwitch->setColor(FontColor);
    labelDirectionSwitch->setText(UTF8("方向开关"));
    labelDirectionSwitch->setFontSize(30);
    labelDirectionSwitch->setTextAlignment(CATextAlignmentCenter);
    labelDirectionSwitch->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    this->getView()->addSubview(labelDirectionSwitch);

    m_DirectionSwitch =  CASwitch::createWithLayout(DLayout(DHorizontalLayout_L_W(250, 100), DVerticalLayout_T_H(100, 20)));
    m_DirectionSwitch->setTag(102);
    var = m_StepMotorHardware.getDirection();
    m_DirectionSwitch->setIsOn(var==0?false:true, false);
    m_DirectionSwitch->addTarget(this, CAControl_selector(StepMotorControlView::switchStateChange));
    this->getView()->addSubview(m_DirectionSwitch);


    //CAView* view1 = CAView::createWithLayout(DLayoutFill);
    //view1->setColor(CAColor_gray);

    m_VelocityValue = CALabel::createWithLayout(DLayout(DHorizontalLayout_L_R(100, 100), DVerticalLayout_T_H(200, 50)));
    m_VelocityValue->setColor(FontColor);
    char str[64];
    sprintf(str, "%d%%", m_StepMotorHardware.getVelocity());
    m_VelocityValue->setText(str);
    m_VelocityValue->setFontSize(30);
    m_VelocityValue->setTextAlignment(CATextAlignmentCenter);
    m_VelocityValue->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    this->getView()->addSubview(m_VelocityValue);

    m_Slider = CASlider::createWithLayout(DLayout(DHorizontalLayout_L_R(120, 120), DVerticalLayout_T_H(250, 56)));
    m_Slider->addTarget(this, CAControl_selector(StepMotorControlView::SliderValueChange));
    m_Slider->setTag(100);
    m_Slider->setValue((float)m_StepMotorHardware.getVelocity() / (m_Slider->getMaxValue() * 100));
    this->getView()->addSubview(m_Slider);

    CALabel* labelStepMotorSwitch = CALabel::createWithLayout(DLayout(DHorizontalLayout_L_W(10, 200), DVerticalLayout_T_H(400, 40)));
    labelStepMotorSwitch->setColor(FontColor);
    labelStepMotorSwitch->setText(UTF8("电机开关"));
    labelStepMotorSwitch->setFontSize(30);
    labelStepMotorSwitch->setTextAlignment(CATextAlignmentCenter);
    labelStepMotorSwitch->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    this->getView()->addSubview(labelStepMotorSwitch);

    m_RunSwitch = CASwitch::createWithLayout(DLayout(DHorizontalLayout_L_W(250, 100), DVerticalLayout_T_H(400, 20)));
    m_RunSwitch->setTag(101);
    var = m_StepMotorHardware.getActionState();
    m_RunSwitch->setIsOn(var == 0 ? false : true, false);
    m_RunSwitch->addTarget(this, CAControl_selector(StepMotorControlView::switchStateChange));
    this->getView()->addSubview(m_RunSwitch);

}

void StepMotorControlView::viewDidUnload()
{
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;

    m_StepMotorHardware.SaveToDatabase();
}

void StepMotorControlView::SliderValueChange(CAControl* btn, DPoint point)
{
    char value[20] = "";
    CASlider* p_Slider = (CASlider*)btn;
    int v = (int)(p_Slider->getValue() * 100);
    sprintf(value, "%d%%", v);
    if (p_Slider->getTag()==100) {
        m_VelocityValue->setText(value);
        static int old_val = v;
        if(old_val != v)
        {
            old_val = v;
            m_StepMotorHardware.setVelocity(old_val);
        }

    }

}

void StepMotorControlView::switchStateChange(CAControl* btn, DPoint point)
{
    CASwitch* state = (CASwitch*)btn;


    switch(state->getTag())
    {
    case 101://action
            if (state->isOn())
            {
                //CCLog("switchStateChange 101: false");
                m_StepMotorHardware.setActionSate(1);
            }
            else
            {
                //CCLog("switchStateChange 101: true");
                m_StepMotorHardware.setActionSate(0);
            }
            break;
    case 102://direction
            if (state->isOn())
            {
                //CCLog("switchStateChange 102: false");
                m_StepMotorHardware.setDirection(1);
            }
            else
            {
                //CCLog("switchStateChange 102:true");
                m_StepMotorHardware.setDirection(0);
        }
            break;
    default:
        break;

    }

}

void StepMotorControlView::SendFCSMessage(const std::string& strFCSMsg)
{
    CCLog("not implement");
}

#define MAX_TEXT 256
void StepMotorControlView::SendMessage(const char *pszFormat,...)
{
    char msg[256]={0};
    va_list ap;
    va_start(ap, pszFormat);
#if defined(_WIN32 )
    vsnprintf_s(msg, MAX_TEXT, MAX_TEXT, pszFormat, ap);
#else //#elif defined(__GNUC__)
    vsnprintf(msg, MAX_TEXT, pszFormat, ap);
#endif
    va_end(ap);
    SendFCSMessage(msg);
}

void StepMotorControlView::SendDirMessage(int idx, int direction)
{
    SendMessage("{\nidx:%d,\ndir:%d\n}",idx,direction);
}

void StepMotorControlView::SendLoopMessage(int idx, int loop)
{
    SendMessage("{\nidx:%d,\nloop:%d\n}",idx,loop);
}
void StepMotorControlView::SendRecMessage(int idx, int recording)
{
    SendMessage("{\nidx:%d,\nrecording:%d\n}",idx,recording);
}
void StepMotorControlView::SendStepsMessage(int idx, int steps)
{
    SendMessage("{\nidx:%d,\nsteps:%d\n}",idx,steps);
}

void StepMotorControlView::SendActionMessage(int idx, int action, int direction, int velocity)
{
    SendMessage("{\nidx:%d,\nact:%d,\ndir:%d,\nv:%d\n}",idx,action,direction,velocity);
}

void StepMotorControlView::SendVelocityMessage(int idx, int velocity)
{
    SendMessage("{\nidx:%d,\nv:%d\n}",idx,velocity);
}

到此,电机操作界面部分也可以存取相应的参数了。

四、MQTT协议栈和功能实现

先交待一下大致功能:
本实现将在进入主界面(运行本软件看到的第一个界面——也即控制器面板)后立即读取网络设置,并根据网络设置进行连接,而一旦退出主界面进行其他操作,那么立即断开,当再次进入主界面,则重新连接。

下面是具体实现:

接下来,移植mosquito。

首先在nano-CrossApp\projects\StepMotorController\Classes目录下建一个MQTT空文件夹,把:
1、mosquitto-1.4.9.tar.gz\mosquitto-1.4.9\lib 里面所有的头文件和c文件
2、mosquitto-1.4.9.tar.gz\mosquitto-1.4.9\lib\cpp下的mosquittopp.h、和mosquittopp.cpp
3、mosquitto-1.4.9.tar.gz\mosquitto-1.4.9下的config.h
4、mosquitto-1.4.9.tar.gz\mosquitto-1.4.9\src下的lib_load.h、mosquitto_broker.h、mosquitto_plugin.h、persist.h、uthash.h
都拷贝到MQTT目录下:
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第13张图片
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第14张图片

把MQTT的源码文件加入到VS的工程中,把..\Classes\MQTT加入包含目录:

基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第15张图片

基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第16张图片

编译一下,可以通过,但是链接出现错误:

2>mosquitto.obj : error LNK2019: 无法解析的外部符号 ___WSAFDIsSet@8,该符号在函数 _mosquitto_loop 中被引用
2>mosquitto.obj : error LNK2019: 无法解析的外部符号 __imp__closesocket@4,该符号在函数 __mosquitto_connect_init 中被引用
2>net_mosq.obj : error LNK2001: 无法解析的外部符号 __imp__closesocket@4
2>mosquitto.obj : error LNK2019: 无法解析的外部符号 __imp__recv@16,该符号在函数 _mosquitto_loop 中被引用
2>net_mosq.obj : error LNK2001: 无法解析的外部符号 __imp__recv@16
2>mosquitto.obj : error LNK2019: 无法解析的外部符号 __imp__select@20,该符号在函数 _mosquitto_loop 中被引用
2>mosquitto.obj : error LNK2019: 无法解析的外部符号 __imp__WSAGetLastError@0,该符号在函数 _mosquitto_loop 中被引用
2>net_mosq.obj : error LNK2001: 无法解析的外部符号 __imp__WSAGetLastError@0
2>mosquittopp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) const mosqpp::mosquittopp::`vftable'" (__imp_??_7mosquittopp@mosqpp@@6B@),该符号在函数 "public: __thiscall mosqpp::mosquittopp::mosquittopp(char const *,bool)" (??0mosquittopp@mosqpp@@QAE@PBD_N@Z) 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__accept@12,该符号在函数 __mosquitto_socketpair 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__bind@12,该符号在函数 __mosquitto_socketpair 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__connect@12,该符号在函数 __mosquitto_socketpair 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__ioctlsocket@12,该符号在函数 __mosquitto_socket_nonblock 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__getsockname@12,该符号在函数 __mosquitto_socketpair 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__htonl@4,该符号在函数 __mosquitto_socketpair 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__htons@4,该符号在函数 __mosquitto_try_connect 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__listen@8,该符号在函数 __mosquitto_socketpair 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__send@16,该符号在函数 __mosquitto_net_write 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__socket@12,该符号在函数 __mosquitto_socketpair 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__WSAStartup@8,该符号在函数 __mosquitto_net_init 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__WSACleanup@0,该符号在函数 __mosquitto_net_cleanup 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__getaddrinfo@16,该符号在函数 __mosquitto_try_connect 中被引用
2>net_mosq.obj : error LNK2019: 无法解析的外部符号 __imp__freeaddrinfo@4,该符号在函数 __mosquitto_try_connect 中被引用
2>net_mosq.obj : error LNK2001: 无法解析的外部符号 _in6addr_loopback
2>C:\download\nano-CrossApp\projects\StepMotorController\proj.win32\Debug.win32\StepMotorController.exe : fatal error LNK1120: 21 个无法解析的外部命令
========== 生成:  成功 1 个,失败 1 个,最新 2 个,跳过 0 个 ==========

大多数的错误是因为缺少一个库:ws2_32.lib,在附加依赖项中加上即可:
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第17张图片

F7再编译一下,还有一个错误:

2>mosquittopp.obj : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) const mosqpp::mosquittopp::`vftable'" (__imp_??_7mosquittopp@mosqpp@@6B@),该符号在函数 "public: __thiscall mosqpp::mosquittopp::mosquittopp(char const *,bool)" (??0mosquittopp@mosqpp@@QAE@PBD_N@Z) 中被引用
2>C:\download\nano-CrossApp\projects\StepMotorController\proj.win32\Debug.win32\StepMotorController.exe : fatal error LNK1120: 1 个无法解析的外部命令

这个错误原因在mosquittopp.h中对dll导入声明上:

#ifdef _WIN32
#   ifdef mosquittopp_EXPORTS
#       define mosqpp_EXPORT  __declspec(dllexport)
#   else
#       define mosqpp_EXPORT  __declspec(dllimport)
#   endif
#else
#   define mosqpp_EXPORT
#endif

因为我们用的是mosquitto的源码,而不是dll库,所以,应该把这个声明去掉。改为:

#define mosqpp_EXPORT

F7编译链接, 通过。

另外,mosquitto.h中的libmosq_EXPORT也如此处理:

#define libmosq_EXPORT

再有,还要在nano-CrossApp\projects\StepMotorController\Classes\MQTT\config.h中加入:

#define WITH_THREADING

以支持线程操作,否则,会在后面启动前程后不断出现异常。

接下来就是使用mosqpp::mosquittopp类写个派生类MQTT,来完成我的目标。
我们用MQTT类收发MQTT协议消息的时候,需要起另外的线程。
原CAThread类的退出线程的方式不符合我们要求,甚至导致崩溃,因此我们需要照葫芦画瓢,写个PlatformThread类:

PlatformThread.h:

#ifndef __CAPLATFORM_THREAD_H__
#define __CAPLATFORM_THREAD_H__

#include "platform/CCPlatformMacros.h"
#include "ccMacros.h"
#include "basics/CASyncQueue.h"
#include 


class PlatformThreadDelegate
{
public:
    virtual ~PlatformThreadDelegate(){}
    virtual void run()=0;
};


enum PlatformThreadRunType
{
    PlatformThreadRunDirectly,
    PlatformThreadRunNotify
};

typedef bool (*THREAD_PROC_FUNC_t)(void* lpParameter);

class PlatformThread
{
public:
    PlatformThread(PlatformThreadDelegate* thread_delegate );
    virtual ~PlatformThread();

    void start();
    void startAndWait(THREAD_PROC_FUNC_t func);
    void notifyRun(void* param);
    void clear(bool bFree=false);
    int join();
    void close();
    void closeAtOnce();
    void setMaxMsgCount(int v);

    bool isRunning();

    //-- put the initialization code here.
    virtual void OnInitInstance() {}

    ////-- put the main code of the thread here.
    //virtual void OnRunning() {}

    //-- put the cleanup code here.
    virtual void OnExitInstance() {}
    PlatformThreadDelegate* delegate(){return delegate_;}
protected:
    PlatformThreadDelegate* delegate_;
private:
    static void* _ThreadProc(void* lpParameter);

    pthread_t m_hThread;

    pthread_mutex_t m_SleepMutex;
    pthread_cond_t m_SleepCondition;

    int m_iMaxMsgCount;
    CrossApp::CASyncQueue<void*> m_ThreadDataQueue;

    THREAD_PROC_FUNC_t m_pThreadFunc;

    bool m_bIsRunning;

    PlatformThreadRunType m_ThreadRunType;
};




#endif   

PlatformThread.cpp:

#include "PlatformThread.h"
#ifndef usleep

#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
#include "the_third_party/websockets/include/win32/libwebsockets.h"
#elif  CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "the_third_party/websockets/include/android/libwebsockets.h"
#elif CC_TARGET_PLATFORM == CC_PLATFORM_IOS
#include "the_third_party/websockets/include/ios/libwebsockets.h"
#endif

#endif




PlatformThread::PlatformThread(PlatformThreadDelegate* thread_delegate )
:delegate_(thread_delegate) 
, m_bIsRunning(false)
, m_pThreadFunc(NULL)
, m_iMaxMsgCount(32)
{
    pthread_mutex_init(&m_SleepMutex, NULL);
    pthread_cond_init(&m_SleepCondition, NULL);
}

PlatformThread::~PlatformThread()
{
    close();
    pthread_mutex_destroy(&m_SleepMutex);
    pthread_cond_destroy(&m_SleepCondition);
}

void PlatformThread::start()
{
    m_ThreadRunType = PlatformThreadRunDirectly;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8)
    pthread_create(&m_hThread, NULL, _ThreadProc, this);
#endif
}

void PlatformThread::startAndWait(THREAD_PROC_FUNC_t func)
{
    m_ThreadRunType = PlatformThreadRunNotify;
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8)
    pthread_create(&m_hThread, NULL, _ThreadProc, this);
#endif
    m_pThreadFunc = func;
}

void PlatformThread::notifyRun(void* param)
{
    if (m_ThreadDataQueue.GetCount() < m_iMaxMsgCount)
    {
        m_ThreadDataQueue.AddElement(param);
    }
}

void PlatformThread::clear(bool bFree)
{
    if (bFree)
    {
        std::vector< void* > v = m_ThreadDataQueue.GetQueueElements();
        for (int i = 0; i < v.size(); i++)
        {
            CC_SAFE_FREE(v[i]);
        }
    }
    else
    {
        m_ThreadDataQueue.Clear();
    }
}

int PlatformThread::join()
{
    return pthread_join(m_hThread, NULL);
}

void PlatformThread::close()
{
    if (m_bIsRunning)
    {
        m_bIsRunning = false;
        pthread_cond_wait(&m_SleepCondition, &m_SleepMutex);
        pthread_detach(m_hThread);
    }
}

void PlatformThread::closeAtOnce()
{
    m_ThreadDataQueue.Clear();
    close();
}

void PlatformThread::setMaxMsgCount(int v)
{
    m_iMaxMsgCount = v;
}

bool PlatformThread::isRunning()
{
    return m_bIsRunning;
}

void* PlatformThread::_ThreadProc(void* lpParameter)
{
    PlatformThread *pAThread = (PlatformThread*)lpParameter;
    CCAssert(pAThread != NULL, "");

    pAThread->m_bIsRunning = true;
    pAThread->OnInitInstance();

    pthread_mutex_lock(&pAThread->m_SleepMutex);
    while (pAThread->m_bIsRunning)
    {
        if (pAThread->m_ThreadRunType == PlatformThreadRunDirectly)
        {
            if(pAThread->delegate())
                pAThread->delegate()->run();
            //pAThread->OnRunning();
        }
        else if (pAThread->m_ThreadRunType == PlatformThreadRunNotify)
        {
            void* param = NULL;
            if (pAThread->m_ThreadDataQueue.PopElement(param))
            {
                if (pAThread->m_pThreadFunc)
                {
                    if (!pAThread->m_pThreadFunc(param))
                        break;
                }
            }
            else
            {
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
                Sleep(5);
#else
                usleep(5000);
#endif
            }
        }
        else break;
    }

    pthread_mutex_unlock(&pAThread->m_SleepMutex);
    pAThread->OnExitInstance();
    pAThread->m_bIsRunning = false;
    pthread_cond_signal(&pAThread->m_SleepCondition);
    pAThread->join();
    //pthread_exit((void *)0);
    return 0;
}

接下来就是真正实现MQTT的类:
MQTT.h

#pragma once

#include "CrossApp.h"
#include "mosquittopp.h"

#include "PlatformThread.h"

#define CLIENTID    "FC"
#define TOPIC_FCS_OUT   "FCS/out" //FilmCenterSettings
#define TOPIC_FCS_IN    "FCS/in"
#define TOPIC_FCA_OUT   "FCA/out" //FilmCenterAction
#define TOPIC_FCA_IN    "FCA/in"

class MQTT :mosqpp::mosquittopp,public PlatformThreadDelegate
{
public:
    MQTT(const std::string &IPAddress, const unsigned short usIPPort, const std::string &Username, const std::string &Password, const std::string &CAFile);//CAFile是证书文件名,供SSL验证用的
    ~MQTT(void);
    bool isConnected(){ return m_IsConnected; };

    void on_connect(int rc);
    void on_disconnect(int rc);
    void on_message(const struct mosquitto_message *message);
    void on_subscribe(int mid, int qos_count, const int *granted_qos);

    void SendMessage(const std::string &Topic, const std::string &Message);

    bool m_bDoReconnect;
    bool m_IsConnected;

public:
    void run(){Do_Work();}
    bool StartHardware();
    bool StopHardware();
    bool IsHardwareStarted(){return m_bIsMQTTStarted;}
private:

    bool ConnectInt();
    bool ConnectIntEx();
    void ProcessMySensorsMessage(const std::string &MySensorsMessage);
protected:
    std::string m_szIPAddress;
    unsigned short m_usIPPort;
    std::string m_UserName;
    std::string m_Password;
    std::string m_CAFilename;
    void StopMQTT();
    void Do_Work();
    volatile bool m_stoprequested;
    volatile bool m_exitrequested;

private:
    CALock m_Lock;
    bool m_bIsMQTTStarted;
protected:
    PlatformThread* thread_;
};

MQTT.cpp:


#include "MQTT.h"
#include 

#define RETRY_DELAY 30


#define QOS         1

#ifdef WIN32
    #include 
#else
    #include 
#endif

void sleep_seconds(unsigned int seconds)
{
#ifdef WIN32
                Sleep(seconds*1000);
#else
                sleep(seconds);
#endif
}



void sleep_milliseconds(unsigned int ms)
{
#ifdef WIN32
                Sleep(ms);
#else
                usleep(ms*1000);
#endif
}

MQTT::MQTT(const std::string &IPAddress, const unsigned short usIPPort, const std::string &Username, const std::string &Password, const std::string &CAfilename) :
m_szIPAddress(IPAddress),
m_UserName(Username),
m_Password(Password),
m_CAFilename(CAfilename),
m_bIsMQTTStarted(false)
{
    m_IsConnected = false;
    m_bDoReconnect = false;
    mosqpp::lib_init();

    m_stoprequested=false;
    m_exitrequested=false;//...
    m_usIPPort=usIPPort;
}

MQTT::~MQTT(void)
{
    mosqpp::lib_cleanup();
}

bool MQTT::StartHardware()
{
    thread_ = new PlatformThread(this);
    if(!thread_)
    {
        CCLog("error: thread_=NULL!");
        return false;
    }

    m_stoprequested=false;

    //force connect the next first time
    m_IsConnected = false;
    //if(!isRunning())
    //  PlatformThread::start();
    //else
    //{
    //  resume();
    //}
    m_bIsMQTTStarted = true;


    thread_->start();

    return true;
}

void MQTT::StopMQTT()
{
    disconnect();
}

bool MQTT::StopHardware()
{
    m_stoprequested=true;
    //if (m_sConnection.connected())
    //  m_sConnection.disconnect();
    m_IsConnected = false;
    m_bIsMQTTStarted = false;
    StopMQTT();
    if(thread_)
    {   
        delete thread_;
        thread_ = NULL;
    }
    return true;
}

void MQTT::on_subscribe(int mid, int qos_count, const int *granted_qos)
{
    CCLog("MQTT: Subscribed");
    m_IsConnected = true;
}

void MQTT::on_connect(int rc)
{
    /* rc=
    ** 0 - success
    ** 1 - connection refused(unacceptable protocol version)
    ** 2 - connection refused(identifier rejected)
    ** 3 - connection refused(broker unavailable)
    */

    if (rc == 0){
        if (m_IsConnected) {
            CCLog("MQTT: re-connected to: %s:%ld", m_szIPAddress.c_str(), m_usIPPort);
        } else {
            CCLog("MQTT: connected to: %s:%ld", m_szIPAddress.c_str(), m_usIPPort);
            m_IsConnected = true;
            //sOnConnected(this);
        }
        subscribe(NULL, TOPIC_FCS_IN);//.....
        subscribe(NULL, TOPIC_FCA_IN);
    }
    else {
        CCLog("MQTT: Connection failed!, restarting (rc=%d)",rc);
        m_bDoReconnect = true;
    }
}

void MQTT::on_message(const struct mosquitto_message *message)
{
    std::string topic = message->topic;
    std::string qMessage = std::string((char*)message->payload, (char*)message->payload + message->payloadlen);

    CCLog("MQTT: Topic: %s, Message: %s", topic.c_str(), qMessage.c_str());

    if (qMessage.empty())
        return;

    //收到消息后处理过程

    if (topic == TOPIC_FCS_IN ||topic == TOPIC_FCA_IN)
    {
        ProcessMySensorsMessage(qMessage);
    }
}

void MQTT::on_disconnect(int rc)
{
    if (rc != 0)
    {
        if (!m_stoprequested)
        {
            if (rc == 5)
            {
                CCLog("MQTT: disconnected, Invalid Username/Password (rc=%d)", rc);
            }
            else
            {
                CCLog("MQTT: disconnected, restarting (rc=%d)", rc);
            }
            m_bDoReconnect = true;
        }
    }
}


bool MQTT::ConnectInt()
{
    StopMQTT();
    return ConnectIntEx();
}

bool MQTT::ConnectIntEx()
{
    m_bDoReconnect = false;
    CCLog("MQTT: Connecting to %s:%d", m_szIPAddress.c_str(), m_usIPPort);

    int rc;
    int keepalive = 60;

    if (!m_CAFilename.empty()){
        rc = tls_set(m_CAFilename.c_str());

        if ( rc != MOSQ_ERR_SUCCESS)
        {
            CCLog("MQTT: Failed enabling TLS mode, return code: %d (CA certificate: '%s')", rc, m_CAFilename.c_str());
            return false;
        } else {
            CCLog("MQTT: enabled TLS mode");
        }
    }
    rc = username_pw_set((!m_UserName.empty()) ? m_UserName.c_str() : NULL, (!m_Password.empty()) ? m_Password.c_str() : NULL);

    rc = connect(m_szIPAddress.c_str(), m_usIPPort, keepalive);
    if ( rc != MOSQ_ERR_SUCCESS)
    {
        CCLog("MQTT: Failed to start, return code: %d (Check IP/Port)", rc);
        m_bDoReconnect = true;
        return false;
    }
    return true;
}

void MQTT::Do_Work()
{
    bool bFirstTime=true;
    int msec_counter = 0;
    int sec_counter = 0;

    while (!m_stoprequested)
    {
        sleep_milliseconds(100);
        if (!bFirstTime)
        {
            int rc = loop();
            if (rc) {
                if (rc != MOSQ_ERR_NO_CONN)
                {
                    if (!m_stoprequested)
                    {
                        if (!m_bDoReconnect)
                        {
                            reconnect();
                        }
                    }
                }
            }
        }

        msec_counter++;
        if (msec_counter == 10)
        {
            msec_counter = 0;
            sec_counter++;

            if (bFirstTime)
            {
                bFirstTime = false;
                ConnectInt();
            }
            else
            {
                if (sec_counter % 30 == 0)
                {
                    if (m_bDoReconnect)
                        ConnectIntEx();
                }
            }
        }
    }
    CCLog("MQTT: Worker stopped...");
}

void MQTT::SendMessage(const std::string &Topic, const std::string &Message)
{
    try {
        if (!m_IsConnected)
        {
            CCLog("MQTT: Not Connected, failed to send message: %s", Message.c_str());
            return;
        }
        publish(NULL, Topic.c_str(), Message.size(), Message.c_str());
    }
    catch (...)
    {
        CCLog("MQTT: Failed to send message: %s", Message.c_str());
    }
}


void MQTT::ProcessMySensorsMessage(const std::string &MySensorsMessage)
{
    //有待进一步实现
    CCLog("MQTT::ProcessMySensorsMessage not implement.");
}

接下来,要修改一下StepMotorControlView类:
StepMotorControlView.h:


#ifndef __StepMotorControlView_h__
#define __StepMotorControlView_h__

#include 
#include "CrossApp.h"
#include "CrossAppExt.h"
#include "SettingsHelper.h"
#include "MQTT.h"

USING_NS_CC;

class StepMotorControlView : public CAViewController
{

public:

    StepMotorControlView();

    virtual ~StepMotorControlView();

protected:

    void viewDidLoad();

    void viewDidUnload();

    virtual void viewDidAppear();
    virtual void viewDidDisappear();
private:
    void SliderValueChange(CAControl* btn, DPoint point);
    CALabel* m_VelocityValue;
    CASlider* m_Slider;  

    void switchStateChange(CAControl* btn, DPoint point);
    CASwitch* m_RunSwitch;
    CASwitch* m_DirectionSwitch;

public:
    void SendFCSMessage(const std::string& strFCSMsg);//FilmCenterSettings message
    void SendMessage(const char *pszFormat,...);
    void SendDirMessage(int idx, int direction);
    void SendLoopMessage(int idx, int loop);
    void SendRecMessage(int idx, int recording);
    void SendStepsMessage(int idx, int recording);
    void SendActionMessage(int idx, int action,int direction,int velocity);
    void SendVelocityMessage(int idx, int velocity);
private:
    EquipmentINFO m_StepMotorHardware;
    MQTT * m_MQTTInstance;
};


#endif /* defined(__HelloCpp__ViewController__) */

StepMotorControlView.cpp修改后如下:


#include "StepMotorControlView.h"
#include "platform/CACommon.h"

#define FontColor ccc4(51,204,255,255) //ccc4(255,255,255,255)


StepMotorControlView::StepMotorControlView()
    :m_Slider(NULL), m_StepMotorHardware("mainstepmotor"), m_MQTTInstance(NULL)
{

}

StepMotorControlView::~StepMotorControlView()
{
    CADrawerController* drawer = (CADrawerController*)CAApplication::getApplication()->getRootWindow()->getRootViewController();
    drawer->setTouchMoved(true);
    viewDidDisappear();
    m_StepMotorHardware.SaveToDatabase();
}

void StepMotorControlView::viewDidLoad()
{

    CAImageView* view1 = CAImageView::createWithImage(CAImage::create("image/motor_control_view.jpg"));
    view1->setLayout(DLayoutFill);
    this->getView()->addSubview(view1);

    int var=0;

    CALabel* labelDirectionSwitch = CALabel::createWithLayout(DLayout(DHorizontalLayout_L_W(10, 200), DVerticalLayout_T_H(100, 40)));
    labelDirectionSwitch->setColor(FontColor);
    labelDirectionSwitch->setText(UTF8("方向开关"));
    labelDirectionSwitch->setFontSize(30);
    labelDirectionSwitch->setTextAlignment(CATextAlignmentCenter);
    labelDirectionSwitch->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    this->getView()->addSubview(labelDirectionSwitch);

    m_DirectionSwitch =  CASwitch::createWithLayout(DLayout(DHorizontalLayout_L_W(250, 100), DVerticalLayout_T_H(100, 20)));
    m_DirectionSwitch->setTag(102);
    var = m_StepMotorHardware.getDirection();
    m_DirectionSwitch->setIsOn(var==0?false:true, false);
    m_DirectionSwitch->addTarget(this, CAControl_selector(StepMotorControlView::switchStateChange));
    this->getView()->addSubview(m_DirectionSwitch);


    //CAView* view1 = CAView::createWithLayout(DLayoutFill);
    //view1->setColor(CAColor_gray);

    m_VelocityValue = CALabel::createWithLayout(DLayout(DHorizontalLayout_L_R(100, 100), DVerticalLayout_T_H(200, 50)));
    m_VelocityValue->setColor(FontColor);
    char str[64];
    sprintf(str, "%d%%", m_StepMotorHardware.getVelocity());
    m_VelocityValue->setText(str);
    m_VelocityValue->setFontSize(30);
    m_VelocityValue->setTextAlignment(CATextAlignmentCenter);
    m_VelocityValue->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    this->getView()->addSubview(m_VelocityValue);

    m_Slider = CASlider::createWithLayout(DLayout(DHorizontalLayout_L_R(120, 120), DVerticalLayout_T_H(250, 56)));
    m_Slider->addTarget(this, CAControl_selector(StepMotorControlView::SliderValueChange));
    m_Slider->setTag(100);
    m_Slider->setValue((float)m_StepMotorHardware.getVelocity() / (m_Slider->getMaxValue() * 100));
    this->getView()->addSubview(m_Slider);

    CALabel* labelStepMotorSwitch = CALabel::createWithLayout(DLayout(DHorizontalLayout_L_W(10, 200), DVerticalLayout_T_H(400, 40)));
    labelStepMotorSwitch->setColor(FontColor);
    labelStepMotorSwitch->setText(UTF8("电机开关"));
    labelStepMotorSwitch->setFontSize(30);
    labelStepMotorSwitch->setTextAlignment(CATextAlignmentCenter);
    labelStepMotorSwitch->setVerticalTextAlignmet(CAVerticalTextAlignmentCenter);
    this->getView()->addSubview(labelStepMotorSwitch);

    m_RunSwitch = CASwitch::createWithLayout(DLayout(DHorizontalLayout_L_W(250, 100), DVerticalLayout_T_H(400, 20)));
    m_RunSwitch->setTag(101);
    var = m_StepMotorHardware.getActionState();
    m_RunSwitch->setIsOn(var == 0 ? false : true, false);
    m_RunSwitch->addTarget(this, CAControl_selector(StepMotorControlView::switchStateChange));
    this->getView()->addSubview(m_RunSwitch);

}

void StepMotorControlView::viewDidUnload()
{
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;

    m_StepMotorHardware.SaveToDatabase();
}

void StepMotorControlView::viewDidAppear()
{
    if (!m_MQTTInstance)
    {
        NetworkINFO tNetworkInfo;
        m_MQTTInstance = new MQTT(tNetworkInfo.getIP(), tNetworkInfo.getPort(), tNetworkInfo.getUsername(), tNetworkInfo.getPassword(), std::string());
    }
    if (!m_MQTTInstance->IsHardwareStarted())
        m_MQTTInstance->StartHardware();
}

void StepMotorControlView::viewDidDisappear()
{
    if (m_MQTTInstance && m_MQTTInstance->IsHardwareStarted())
    {
        m_StepMotorHardware.setActionSate(0);
        SendActionMessage(m_StepMotorHardware.getIDX(), m_StepMotorHardware.getActionState(),m_StepMotorHardware.getDirection(),m_StepMotorHardware.getVelocity());
        m_MQTTInstance->StopHardware();
    }
}

void StepMotorControlView::SliderValueChange(CAControl* btn, DPoint point)
{
    char value[20] = "";
    CASlider* p_Slider = (CASlider*)btn;
    int v = (int)(p_Slider->getValue() * 100);
    sprintf(value, "%d%%", v);
    if (p_Slider->getTag()==100) {
        m_VelocityValue->setText(value);
        static int old_val = v;
        if(old_val != v)
        {
            old_val = v;
            m_StepMotorHardware.setVelocity(old_val);
            if (m_MQTTInstance && m_MQTTInstance->IsHardwareStarted())
            {
                SendVelocityMessage( m_StepMotorHardware.getIDX(),m_StepMotorHardware.getVelocity() );
            }
        }

    }

}

void StepMotorControlView::switchStateChange(CAControl* btn, DPoint point)
{
    CASwitch* state = (CASwitch*)btn;


    switch(state->getTag())
    {
    case 101://action
            if (state->isOn())
            {
                //CCLog("switchStateChange 101: false");
                m_StepMotorHardware.setActionSate(1);
            }
            else
            {
                //CCLog("switchStateChange 101: true");
                m_StepMotorHardware.setActionSate(0);
            }

            if (m_MQTTInstance && m_MQTTInstance->IsHardwareStarted())
            {
                SendActionMessage(m_StepMotorHardware.getIDX(), m_StepMotorHardware.getActionState(), m_StepMotorHardware.getDirection(), m_StepMotorHardware.getVelocity());
            }
            break;
    case 102://direction
            if (state->isOn())
            {
                //CCLog("switchStateChange 102: false");
                m_StepMotorHardware.setDirection(1);
            }
            else
            {
                //CCLog("switchStateChange 102:true");
                m_StepMotorHardware.setDirection(0);
            }
            if (m_MQTTInstance && m_MQTTInstance->IsHardwareStarted())
            {
                SendDirMessage( m_StepMotorHardware.getIDX(),m_StepMotorHardware.getDirection() );
            }
            break;
    default:
        break;

    }

}

void StepMotorControlView::SendFCSMessage(const std::string& strFCSMsg)
{
    if (m_MQTTInstance && m_MQTTInstance->IsHardwareStarted())
    {
        m_MQTTInstance->SendMessage(TOPIC_FCS_OUT, strFCSMsg);
    }
}

#define MAX_TEXT 256
void StepMotorControlView::SendMessage(const char *pszFormat,...)
{
    char msg[256]={0};
    va_list ap;
    va_start(ap, pszFormat);
#if defined(_WIN32 )
    vsnprintf_s(msg, MAX_TEXT, MAX_TEXT, pszFormat, ap);
#else //#elif defined(__GNUC__)
    vsnprintf(msg, MAX_TEXT, pszFormat, ap);
#endif
    va_end(ap);
    SendFCSMessage(msg);
}

void StepMotorControlView::SendDirMessage(int idx, int direction)
{
    SendMessage("{\nidx:%d,\ndir:%d\n}",idx,direction);
}

void StepMotorControlView::SendLoopMessage(int idx, int loop)
{
    SendMessage("{\nidx:%d,\nloop:%d\n}",idx,loop);
}
void StepMotorControlView::SendRecMessage(int idx, int recording)
{
    SendMessage("{\nidx:%d,\nrecording:%d\n}",idx,recording);
}
void StepMotorControlView::SendStepsMessage(int idx, int steps)
{
    SendMessage("{\nidx:%d,\nsteps:%d\n}",idx,steps);
}

void StepMotorControlView::SendActionMessage(int idx, int action, int direction, int velocity)
{
    SendMessage("{\nidx:%d,\nact:%d,\ndir:%d,\nv:%d\n}",idx,action,direction,velocity);
}

void StepMotorControlView::SendVelocityMessage(int idx, int velocity)
{
    SendMessage("{\nidx:%d,\nv:%d\n}",idx,velocity);
}

编译链接后运行,测试一下结果:
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第18张图片

基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第19张图片

联合开发板做测试:
基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第20张图片

基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制(六)基于CrossApp跨平台框架的MQTT客户端控制应用PC版_第21张图片

本版完整源码地址:
StepMotorController_2017.10.02_VS2013-OK.rar

下一篇讲在本篇基础上,完善平台兼容性,实现安卓版,并在手机上运行试验。

你可能感兴趣的:(CrossApp,物联网与MQTT)