本文详细介绍以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协议栈和功能实现。
以下是具体内容:
双击project-creator.exe:
工程名填:StepMotorController
package Name那里中间填公司名,本项目随便填了一个MyCompany,最右边填App名字,本项目填StepMotorController
然后点击Create Project Now。
跳出一个对话框来,点确定,然后看到:
然后关闭。
看到nano-CrossApp目录下出现一个projects文件夹,这个就是我们项目存放的目录,进去之后看到StepMotorController文件夹,里面就是我们的项目了。
我们先做Windows上的版本,然后再进一步兼容其他平台。
先把框架建起来:
然后双击nano-CrossApp\projects\StepMotorController\proj.win32下的StepMotorController.sln,打开vs工程,把StepMotorController设置为启动项,在debug版编译一下,运行调试,看到:
参考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下,作为背景图片。
初步大概的界面效果是:
接下来,把easySQLite移植过来,实现对参数的存取。
把easySQLite_v10.zip\easySQLite\easySQLite下的整个easySQLite拷贝到nano-CrossApp\projects\StepMotorController\Classes目录下。
在vs上的StepMotorCotroller项目的Classes下新建一个筛选器,命名为easySQLite。并把刚才拷贝过来的文件加入进来。
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
}
在 bool SettingsHelper::OpenDatabase() 中打上断点,重新运行到断点,可以看到:
这是我们的数据库存放的具体位置,手动找到这个数据库如下:
退出后重进,可以看到前面保存的参数已经读出来了。
然后在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);
}
到此,电机操作界面部分也可以存取相应的参数了。
先交待一下大致功能:
本实现将在进入主界面(运行本软件看到的第一个界面——也即控制器面板)后立即读取网络设置,并根据网络设置进行连接,而一旦退出主界面进行其他操作,那么立即断开,当再次进入主界面,则重新连接。
下面是具体实现:
接下来,移植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目录下:
把MQTT的源码文件加入到VS的工程中,把..\Classes\MQTT加入包含目录:
编译一下,可以通过,但是链接出现错误:
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,在附加依赖项中加上即可:
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);
}
本版完整源码地址:
StepMotorController_2017.10.02_VS2013-OK.rar
下一篇讲在本篇基础上,完善平台兼容性,实现安卓版,并在手机上运行试验。