📄 cppcontrol.cpp
字号:
// CPPControl.cpp : Simple Set and Get of flags using a console.
// The application asks the user for connection information and then allows line
// by line input to the device. This is an example of how a non-MFC C++ program
// can directly use the Control and Status Interfaces, and by inference, all other
// interfaces. Note that this uses Status using polling, an example of Status using
// events can be found in CPPStatus project.
//
// Include Files
// STL for strings and IO
#include <string>
#include <iostream>
#include <sstream>
#include <limits>
// using namespace std; // uncomment this statement to avoid putting std:: everywhere
// ATL for easier use of COM
#define _WIN32_DCOM // Before windows.h, allows CoInitializeEx()
#include <windows.h>
#include <atlbase.h>
CComModule _Module;
#include <atlcom.h>
#undef max // max is a windows macro (bad), but I need to use the max in STL
// Import the BoxBridge Type Library to make COM stuff a bit easier to read
#import "..\ComACRsrvr.tlb" no_namespace named_guids
// Simple Struct/Class to hold/pass communications settings between classes.
// An alturnitive to passing this state information around would be to either
// pass around pointers (as CPPTerminal does) or make the Interfaces global.
struct ConnectData
{
ConnectData():transport(0){ };
ConnectData(const ConnectData &data){ assign(data); };
long transport; // type of transport chosen
long busType; // specific Bus Type (PCI=0, ISA=1)
long portNbr; // specific Com Port
long portBPS; // speed of Com Port
long cardNbr; // identifying number for devices in bus or daisy chained or USB
std::string netIP; // ethernet address
long fullBPS() { long bps[3] = {9600,19200,38400}; if(portBPS>2) portBPS = 2; return bps[portBPS]; }
const ConnectData& operator=(const ConnectData &data){ assign(data); return *this; }
void assign(const ConnectData &other){
transport = other.transport;
busType = other.busType;
portNbr = other.portNbr;
portBPS = other.portBPS;
cardNbr = other.cardNbr;
netIP = other.netIP;
}
};
class ACRparser
{
// The ACRparser will determine if this is a SET or PRINT operation
// The first character dictates what is done. The following characters
// are the flag number.
// 1st Character: + Set Bit Number that follows, e.g. +128 sets bit 128
// 1st Character: - Clr Bit Number that follows, e.g. -128 clears bit 128
// 1st Character: ? Get Bit Number that follows, e.g. ?128 prints SET or CLR
// 1st Character anything else, return string
public:
enum FIRST_CHAR {FLAG_SET='+', FLAG_CLR='-', FLAG_PRN='?'};
ACRparser(ConnectData cData)
{
// Try to connect when created
CoInitializeEx(NULL, COINIT_MULTITHREADED);
m_Cntl.CreateInstance(CLSID_Control);
m_Stat.CreateInstance(CLSID_Status);
// Connect to Control Interface
m_Cntl->nBus = cData.busType;
m_Cntl->nPort = cData.portNbr;
m_Cntl->nBPS = cData.fullBPS();
m_Cntl->bstrIP = _bstr_t(cData.netIP.c_str());
m_Cntl->Connect(cData.transport,cData.cardNbr);
// Connect to Status Interface
m_Stat->nBus = cData.busType;
m_Stat->nPort = cData.portNbr;
m_Stat->nBPS = cData.fullBPS();
m_Stat->bstrIP = _bstr_t(cData.netIP.c_str());
m_Stat->Connect(cData.transport,cData.cardNbr);
}
virtual ~ACRparser()
{
// Close the COM stuff down
m_Cntl->Disconnect();
m_Cntl = NULL; // Setting smart pointers to NULL is *VERY IMPORTANT*
m_Stat->Disconnect();
m_Stat = NULL; // Setting smart pointers to NULL is *VERY IMPORTANT*
CoUninitialize();
}
std::string DoFlag(const std::string cmd)
{
long bitNbr=0; // Bit Number
long parmNbr=0; // p-Parameter Number
long parmVal=0; // p-Parameter Value
long bitIndex=0; // Index of bit in the p-Parameter
std::ostringstream os;// Used to create string from number + string
SAFEARRAY* pSA;
long lBound = 0;
CComVariant vItem; // The CComVariant is found in the atlbase.h include file
CComVariant status; // it will clean up SAFEARRAY when it goes out of scope.
// Check the first character and process bit number
switch(cmd.at(0)){
case FLAG_SET:
// Use the SetFlag() method in the Control Interface to set the specific bit
m_Cntl->SetFlag(atol(cmd.substr(1).c_str()),true,true);
m_Msg = "flag " + cmd.substr(1) + " now SET.";
break;
case FLAG_CLR:
// Use the SetFlag() method in the Control Interface to clear the specific bit
m_Cntl->SetFlag(atol(cmd.substr(1).c_str()),false,true);
m_Msg = "flag " + cmd.substr(1) + " now CLR.";
break;
case FLAG_PRN:
// Use the GetACRCustom() method in the Status Interface to find the bit's value.
// This requires us to determine the p-parameter the bit is stored in, and at what
// position in the Long p-parameter it occupies. This requires some math, the use
// of a SAFEARRAY and the help of the IsFlagSet() method in the Status Interface.
// Do the Math
bitNbr = atol(cmd.substr(1).c_str());
parmNbr = 4096 + (bitNbr/32);
bitIndex = bitNbr%32;
// Get the value of the p_Parm, returned in a safearray
os << "P" << parmNbr;
status = m_Stat->GetACRCustom(_bstr_t(os.str().c_str()));
if(status.vt & VT_ARRAY){
pSA = status.parray;
SafeArrayGetElement(pSA, &lBound, &vItem);
if(vItem.vt!=VT_EMPTY){
parmVal = vItem.lVal;
}else{
m_Msg = "Problem Getting Status";
break;
}
}else{
m_Msg = "Problem Getting Status";
break;
}
// Check if bit set or clear
if(m_Stat->IsFlagSet(parmVal, bitIndex)){
m_Msg = "flag " + cmd.substr(1) + " is currently SET";
}else{
m_Msg = "flag " + cmd.substr(1) + " is currently CLR";
}
break;
default:
m_Msg = cmd;
break;
}
return m_Msg;
}
private:
std::string m_Msg;
IControlPtr m_Cntl;
IStatusPtr m_Stat;
};
void getInputLong(long &in, const std::string &msg, const long inMin = 0, const long inMax = 0) {
// The cin has problems if the proper type of data is not entered, so
// handel those issues in one place. Since most of the cin is in the
// main(), this is a global function, but could also be a class method.
while (!(std::cin >> in) || ((inMax > inMin) && (in < inMin || in > inMax))) {
std::cout << msg;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
//-----------------------------------------------------------------------------
// Main Control Loop
//-----------------------------------------------------------------------------
int main(int argc, char* argv[])
{
// The Interface pointers are here (before the try) to allow easy clean up in
// main()'s try/catch. It is important to set any smart pointers to NULL before exiting.
IControlPtr cCntl;
IStatusPtr cStat;
try{
// Need to initialize for each thread as multithreaded
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// Establish Communication Server
cCntl.CreateInstance(CLSID_Control);
cStat.CreateInstance(CLSID_Status);
std::string ver = std::string(_com_util::ConvertBSTRToString(cCntl->bstrVersion)); // Version
std::cout << "--------Get and Set Flags using the ComACRsrvr v" << ver << "--------\n\n";
ConnectData cData;
// Collect Connection Information
cData.transport = 0; // transport type
cData.busType = 0; // bus type
cData.portNbr = 1; // com port
cData.portBPS = 2; // bps of com port
cData.netIP = "0.0.0.0";// ip address
cData.cardNbr = 0; // card index
std::string portname; // transport lable
std::string input; // terminal input
std::cout << "How are we connecting. Type in a number and press <enter>\n0=Offline, 1=Bus, 2=Serial, 3=Ethernet, 4=USB: ";
getInputLong(cData.transport, "You must enter a number between 0 - 4: ",0,4);
switch (cData.transport){
// Various transport types require specific extra communication data
case 0:
portname = "OFFLINE";
break;
case 1:
portname = "BUS";
std::cout << "What Type of bus is the device in?\n0=PCI, 1=ISA: ";
getInputLong(cData.busType, "You must enter a number between 0 - 1: ",0,1);
std::cout << "What is the index number of the device (usually 0):";
getInputLong(cData.cardNbr, "You must enter a number: ");
break;
case 2:
portname = "SERIAL";
std::cout << "What is the Com Port number: ";
getInputLong(cData.portNbr, "You must enter a number between 1 - 256: ",1,256);
std::cout << "What is the BPS of the Com Port? 0=9600, 1=19200, 2=38400: ";
getInputLong(cData.portBPS, "You must enter a number between 0 - 2: ",0,2);
std::cout << "What is the index number of the device (usually 0): ";
getInputLong(cData.cardNbr, "You must enter a number: ");
break;
case 3:
portname = "ETHERNET";
std::cout << "What is the ip address of the device: ";
std::cin >> cData.netIP;
std::cout << "What is the index number of the device (usually 0): ";
getInputLong(cData.cardNbr, "You must enter a number: ");
break;
case 4:
portname = "USB";
std::cout << "What is the index number of the device (use 0 if unsure): ";
getInputLong(cData.cardNbr, "You must enter a number: ");
break;
default:
portname = "Unknown";
cData.transport = 0;
break;
}
// We do not actually use the Interfaces in main(), but just connect to verify
// that the connection information is OK.
// Connect to Control Interface
cCntl->nBus = cData.busType;
cCntl->nPort = cData.portNbr;
cCntl->nBPS = cData.fullBPS();
cCntl->bstrIP = _bstr_t(cData.netIP.c_str());
std::cout << "\nConnecting...";
cCntl->Connect(cData.transport,cData.cardNbr);
if(cCntl->isOffline){
std::cout << "you have choosen not to connect, goodbye" << std::endl;
return 0;
}
std::cout << "you are now connected using the " << portname << " port.\n\nEnter the word QUIT (in UPPER CASE) to exit.\n\n";
std::cout << "Flags can be set, cleared or checked for their current state.\nUse a command prefix ";
std::cout << "character plue a bit number, and hit enter.\nThe command prefix can be:\n+ : SET Flag\n- : CLR Flag\n? : Query Flag\nFor example, +128 sets bit 128.\n";
// Connect to Status Interface
cStat->nBus = cData.busType;
cStat->nPort = cData.portNbr;
cStat->nBPS = cData.fullBPS();
cStat->bstrIP = _bstr_t(cData.netIP.c_str());
cStat->Connect(cData.transport,cData.cardNbr);
// The Application - line based data entry (i.e. type in stuff and hit enter to submit.)
ACRparser gParse(cData);
while ((std::cin >> input) && (input != "QUIT")){
std::cout << gParse.DoFlag(input) << std::endl;
}
// Close the COM stuff down
cCntl->Disconnect();
cCntl = NULL; // Setting smart pointers to NULL is *VERY IMPORTANT*
cStat->Disconnect();
cStat = NULL; // Setting smart pointers to NULL is *VERY IMPORTANT*
CoUninitialize();
return 0;
// Error Handling - the errors specifically thrown by Communications Server
// show up as COM exceptions and can be caught with the _com_error class.
}catch(_com_error err){
cCntl = NULL;
cStat = NULL;
CoUninitialize();
MessageBox(NULL, err.Description(), "COM Error", MB_OK);
return 0;
}catch(...){
cCntl = NULL;
cStat = NULL;
CoUninitialize();
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0,NULL);
MessageBox(NULL, (LPCTSTR)lpMsgBuf, "General Error", MB_OK);
LocalFree(lpMsgBuf);
return 0;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -