admin
2022-06-30 a17738e1545ff7dbef6398b8ec1eab93ab59c9a1
'功能完善'
31个文件已修改
3个文件已添加
1988 ■■■■ 已修改文件
ConsoleApplication/CaptureUtil.cpp 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/CaptureUtil.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/ConsoleApplication.vcxproj 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/ImgDivider.cpp 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/ImgDivider.h 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/ImgUtil.cpp 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/ImgUtil.h 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/OpenCLExcuter.cpp 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/OpenCLExcuter.h 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/RecognitionManager.cpp 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/RecognitionManager.h 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/RecognitionUtil.h 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/ScreenDataCapture.cpp 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/ScreenDataCapture.h 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/THSActionUtil.cpp 536 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/THSActionUtil.h 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/Win32Util.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/kernel.cl 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/main.cpp 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/ExcelUtil.cpp 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/JsonUtil.h 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/NetWorkUtil.cpp 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/NetWorkUtil.h 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/SocketManager.cpp 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/SocketManager.h 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/app.aps 补丁 | 查看 | 原始文档 | blame | 历史
app/app.rc 补丁 | 查看 | 原始文档 | blame | 历史
app/app.vcxproj 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/app.vcxproj.filters 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/appDlg.cpp 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/appDlg.h 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/kernel.cl 252 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/level2DataDlg.cpp 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/resource.h 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ConsoleApplication/CaptureUtil.cpp
@@ -1,6 +1,6 @@
#include "CaptureUtil.h"
HWND CaptureUtil::frames[8];
HWND CaptureUtil::level2Frames[8];
std::list<FrameInfo> CaptureUtil::tempFrames;
BOOL CALLBACK CaptureUtil::EnumChildProc(HWND hwndChild, LPARAM lParam) {
@@ -10,8 +10,9 @@
    if (string(className) == "AfxWnd100s") {
        RECT rect;
        GetWindowRect(hwndChild, &rect);
        bool show= IsWindowVisible(hwndChild);
        
        if (rect.right - rect.left < 1000&& rect.right - rect.left>400)
        if (show&&rect.right - rect.left < 1000&& rect.right - rect.left>400)
        {
            FrameInfo info = FrameInfo();
            info.frame = hwndChild;
@@ -32,18 +33,13 @@
    }
    return TRUE;
}
void CaptureUtil::init(std::string winName) {
void CaptureUtil::init(HWND l2win) {
    tempFrames.clear();
    HWND root = FindWindowA(NULL, winName.c_str());
    HWND root = l2win;
    root = FindWindowEx(root, NULL, TEXT("AfxFrameOrView100s"), NULL);
    EnumChildWindows(root, EnumChildProc, NULL);
    //TODO list排序
    //赋值
    std::list<FrameInfo>::iterator ele;
    int index = 0;
@@ -51,11 +47,11 @@
        if (index > 7) {
            break;
        }
        frames[index++] = (*ele).frame;
        level2Frames[index++] = (*ele).frame;
    }
}
HWND CaptureUtil::getHWND(int index) {
    return frames[index];
    return level2Frames[index];
}
cv::Mat CaptureUtil::capture(int index) {
ConsoleApplication/CaptureUtil.h
@@ -17,13 +17,13 @@
{
private:
    static std::list<FrameInfo> tempFrames;
    static HWND frames[8];
    static HWND level2Frames[8];
    static BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam);
    static HWND getHWND(int index);
public:
    //初始化
    static void init(std::string winName);
    static void init(HWND l2win);
    //截图
    static cv::Mat capture(int index);
ConsoleApplication/ConsoleApplication.vcxproj
@@ -41,7 +41,7 @@
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <ConfigurationType>StaticLibrary</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
ConsoleApplication/ImgDivider.cpp
@@ -35,6 +35,22 @@
    return true;
}
bool ImgDivider::isRowFull(cv::Mat img, int row, int startCol, int endCol, int simple,int splitColorFrom, int splitColorTo) {
    int emptyCount = 0;
    for (int i = startCol;i <= endCol;i += simple) {
        uchar data = img.ptr<uchar>(row)[i];
        if (data > splitColorTo|| data< splitColorFrom) {
            //允许最大有10个空数据
            if (emptyCount < 10) {
                emptyCount++;
                continue;
            }
            return false;
        }
    }
    return true;
}
bool ImgDivider::isRowEmpty(cv::Mat img, int row) {
    int  cols = img.cols;
    for (int i = 0;i < cols;i += 1) {
ConsoleApplication/ImgDivider.h
@@ -22,6 +22,9 @@
    static    bool isRowFull(cv::Mat img, int row,int startCol,int endCol, int simple);
    static bool isRowFull(cv::Mat img, int row, int startCol, int endCol, int simple, int splitColorFrom, int splitColorTo);
    //空数据行
    static    bool isRowEmpty(cv::Mat img, int row, int startCol, int endCol);
ConsoleApplication/ImgUtil.cpp
@@ -251,11 +251,11 @@
    }
    return nums;
}
//分隔整数
list<cv::Mat> ImgUtil::splitNum(cv::Mat src)  throw(string) {
list<cv::Mat> ImgUtil::splitNum(cv::Mat src,int threshold_value)  throw(string) {
    cv::Mat binary;
    //去除上下的空白图
    threshold(src, binary, _IMG_BINARY_THRESHOLD, 255, cv::THRESH_BINARY);
    threshold(src, binary, threshold_value , 255, cv::THRESH_BINARY);
    int rows = src.rows;
    int cols = src.cols;
@@ -340,6 +340,11 @@
        }*/
    }
    return resultList;
}
//分隔整数
list<cv::Mat> ImgUtil::splitNum(cv::Mat src)  throw(string) {
    return splitNum(src,_IMG_BINARY_THRESHOLD);
}
cv::Mat  ImgUtil::formatNumLevel2(cv::Mat num)  throw(string) {
@@ -459,6 +464,7 @@
}
list<int*> ImgUtil::divideImg(cv::Mat img, bool save) {
    //cv::imwrite("C:\\Users\\Administrator\\Desktop\\ocr\\demo\\0_gray.jpg", img);
    clock_t start_time = clock();
    LogUtil::info("开始分隔图像");
    list<list<int*>> resultList;
@@ -495,7 +501,7 @@
    }
    if (contentStartRow < 0) {
        LogUtil::error("图像分隔出错:title分隔出错");
        throw string("图像分隔出错:title分隔出错");
    }
    LogUtil::debug("\n\n\n\n");
@@ -757,7 +763,8 @@
            }
        }
        //释放数据
        free(*ele);
    }
ConsoleApplication/ImgUtil.h
@@ -33,7 +33,7 @@
#ifndef _NUMBER_GP_CODE_WIDTH
#define _NUMBER_GP_CODE_WIDTH 11
#define _NUMBER_GP_CODE_WIDTH 12
#endif 
#ifndef _NUMBER_GP_CODE_HEIGHT
@@ -99,6 +99,8 @@
    //分隔整数
    static list<cv::Mat> splitNum(cv::Mat img)  throw(string);
    static list<cv::Mat> splitNum(cv::Mat img,int threshold_value)  throw(string);
    static cv::Mat formatNumGPCode(cv::Mat num)  throw(string);
};
ConsoleApplication/OpenCLExcuter.cpp
@@ -212,7 +212,7 @@
void OpenCLExcuter::splitNum(unsigned char* img, int img_width, int img_height, int* pos, int pos_count, unsigned char* zero, int num_width,int num_height,int num_count, unsigned char* result) {
void OpenCLExcuter::splitL2Num(unsigned char* img, int img_width, int img_height, int* pos, int pos_count, unsigned char* zero, int num_width,int num_height,int num_count, unsigned char* result) {
    clock_t time_0 = clock();
    kernel = clCreateKernel(program, "split_num", &error);
@@ -355,7 +355,7 @@
void OpenCLExcuter::splitRowData(unsigned char* imgs, int imgWidth, int imgHeight, int* rowIndexs, int lines,
void OpenCLExcuter::splitL2RowData(unsigned char* imgs, int imgWidth, int imgHeight, int* rowIndexs, int lines,
    int* result) {
    kernel = clCreateKernel(program, "splitRowData", &error);
@@ -424,6 +424,79 @@
    clReleaseMemObject(memObject3);
}
void OpenCLExcuter::recognitionNotNum(unsigned char* imgs, int imgWidth, int imgHeight, int* rowIndexs,int colCount, int lines,
    int* result) {
    clock_t time_1 = clock();
    kernel = clCreateKernel(program, "recognition_not_num", &error);
    if (kernel == NULL) {
        throw("Couldn't create kernel!\n");
    }
    int inputSize = imgWidth * imgHeight;
    int resultSize = lines * 2;
    //创建缓存对象
    cl_mem memObject1 = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(uchar) * inputSize, imgs, &error);
    if (error < 0) {
        throw("Creat memObject1 failed!\n");
    }
    cl_mem memObject2 = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(int) * lines * 4 * colCount, rowIndexs, &error);
    if (error < 0) {
        throw("Creat memObject2 failed!\n");
    }
    cl_mem memObject3 = clCreateBuffer(context, CL_MEM_WRITE_ONLY,
        sizeof(int) * resultSize, NULL, &error);
    if (error < 0) {
        throw("Creat memObject3 failed!\n");
    }
    //设置内核参数
    error = clSetKernelArg(kernel, 0, sizeof(cl_mem), &memObject1);
    error = clSetKernelArg(kernel, 1, sizeof(cl_mem), &memObject2);
    error |= clSetKernelArg(kernel, 2, sizeof(int), &imgWidth);
    error |= clSetKernelArg(kernel, 3, sizeof(cl_mem), &memObject3);
    if (error != CL_SUCCESS) {
        throw("Error setting kernel arguments!\n");
    }
    //执行内核
    size_t globalWorkSize[1] = { resultSize };
    size_t localWorkSize[1] = { 1 };
    const int work_dim = 1;
    error = clEnqueueNDRangeKernel(queue, kernel, work_dim, NULL, globalWorkSize,
        localWorkSize, 0, NULL, NULL);
    if (error != CL_SUCCESS) {
        throw("Error queuing kernel for execution!\n");
    }
    //读取执行结果
    error = clEnqueueReadBuffer(queue, memObject3, CL_TRUE, 0, resultSize * sizeof(int),
        result, 0, NULL, NULL);
    if (error != CL_SUCCESS) {
        throw("Error reading result buffer!\n");
    }
    //printf("%d %d %d %d \n", result[0], result[1], result[2], result[3]);
    clReleaseKernel(kernel);
    clReleaseMemObject(memObject1);
    clReleaseMemObject(memObject2);
    clReleaseMemObject(memObject3);
}
void OpenCLExcuter::rgb2Gray(unsigned char* imgs, int imgWidth, int imgHeight,
    unsigned char* result) {
@@ -485,6 +558,67 @@
}
void OpenCLExcuter::rgba2Gray(unsigned char* imgs, int imgWidth, int imgHeight,
    unsigned char* result) {
    kernel = clCreateKernel(program, "rgba2GrayImg", &error);
    if (kernel == NULL) {
        throw("Couldn't create kernel!\n");
    }
    int inputSize = imgWidth * imgHeight * 4;
    int resultSize = imgWidth * imgHeight;
    //创建缓存对象
    cl_mem memObject1 = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(uchar) * inputSize, imgs, &error);
    if (error < 0) {
        throw("Creat memObject1 failed!\n");
    }
    cl_mem memObject2 = clCreateBuffer(context, CL_MEM_WRITE_ONLY,
        sizeof(uchar) * resultSize, NULL, &error);
    if (error < 0) {
        throw("Creat memObject2 failed!\n");
    }
    //设置内核参数
    error = clSetKernelArg(kernel, 0, sizeof(cl_mem), &memObject1);
    error |= clSetKernelArg(kernel, 1, sizeof(int), &imgWidth);
    error = clSetKernelArg(kernel, 2, sizeof(cl_mem), &memObject2);
    if (error != CL_SUCCESS) {
        throw("Error setting kernel arguments!\n");
    }
    //执行内核
    size_t globalWorkSize[1] = { imgHeight };
    size_t localWorkSize[1] = { 1 };
    const int work_dim = 1;
    error = clEnqueueNDRangeKernel(queue, kernel, work_dim, NULL, globalWorkSize,
        localWorkSize, 0, NULL, NULL);
    if (error != CL_SUCCESS) {
        throw("Error queuing kernel for execution!\n");
    }
    //读取执行结果
    error = clEnqueueReadBuffer(queue, memObject2, CL_TRUE, 0, resultSize * sizeof(uchar),
        result, 0, NULL, NULL);
    if (error != CL_SUCCESS) {
        throw("Error reading result buffer!\n");
    }
    clReleaseKernel(kernel);
    clReleaseMemObject(memObject1);
    clReleaseMemObject(memObject2);
}
void OpenCLExcuter::destory() {
    //释放资源
    clReleaseDevice(devices);
ConsoleApplication/OpenCLExcuter.h
@@ -30,21 +30,35 @@
public:
    void init();
    void destory();
    unsigned char** recognition_numbers(unsigned char* data, unsigned char* b_in, const int rows, const int cols, const int num_width, const int num_height, const int num_count);
    //分隔每一行需要识别的数字
    void splitNum(unsigned char* img, int img_width, int img_height, int* pos, int pos_count, unsigned char* zero, int num_width, int num_height, int number_count, unsigned char* result);
    //将RGB图片转为灰度图片
    void rgb2Gray(unsigned char* imgs, int imgWidth, int imgHeight,
        unsigned char* result);
    void rgba2Gray(unsigned char* imgs, int imgWidth, int imgHeight,
        unsigned char* result);
    //创建识别的数字模板
    void createNumberTemplates(int lines, int num_width, int num_height, int num_count, uchar* numsOneLevelData, unsigned char* result);
    //识别数字
    unsigned char** recognition_numbers(unsigned char* data, unsigned char* num_templates, const int rows, const int cols, const int num_width, const int num_height, const int num_count);
    //分隔每一行需要识别的数字
    //将连续的数字图片分隔成单个数字的图片,方便识别
    void splitL2Num(unsigned char* img, int img_width, int img_height, int* pos, int pos_count, unsigned char* zero, int num_width, int num_height, int number_count, unsigned char* result);
    //分隔行数据
    void splitRowData(unsigned char* imgs, int imgWidth, int imgHeight, int* rowIndexs, int lines,
    //每一行的数据分隔成7个区域
    void splitL2RowData(unsigned char* imgs, int imgWidth, int imgHeight, int* rowIndexs, int lines,
        int* result);
    void rgb2Gray(unsigned char* imgs, int imgWidth, int imgHeight,
        unsigned char* result);
    //识别非数字
    void recognitionNotNum(unsigned char* imgs, int imgWidth, int imgHeight, int* rowIndexs, int colCount, int lines,
        int* result);
};
ConsoleApplication/RecognitionManager.cpp
@@ -53,11 +53,11 @@
        {
            if (rowData[(index-1) * 4] > 0) {
                //涨停
                (tradeData).limitPrice = true;
                (tradeData).limitPrice = LIMIT_PRICE_UP;
            }
            else
            {
                (tradeData).limitPrice = false;
                (tradeData).limitPrice = LIMIT_PRICE_NORMAL;
            }
        }
        break;
@@ -405,16 +405,16 @@
    //LogUtil::debug("像素值:%d\n", pixelCount);
    if (abs(pixelCount - 39) < 5) {
        return BUY;
        return OPERATE_BUY;
    }
    else if (abs(pixelCount - 51) < 5) {
        return SELL;
        return OPERATE_SELL;
    }
    else if (abs(pixelCount - 105) < 5) {
        return BUY_CANCEL;
        return OPERATE_BUY_CANCEL;
    }
    else if (abs(pixelCount - 117) < 5) {
        return SELL_CANCEL;
        return OPERATE_SELL_CANCEL;
    }
    //pixelSet.insert(pixelCount);
@@ -438,7 +438,7 @@
    //}
    return OPERATE_ERROR;
    return OPERATE_OPERATE_ERROR;
}
std::string RecognitionManager::recognitionPrice(std::string cpath) {
ConsoleApplication/RecognitionManager.h
@@ -13,6 +13,13 @@
#include "OpenCLExcuter.h"
using namespace std;
enum LIMIT_PRICE {
    LIMIT_PRICE_NORMAL,
    LIMIT_PRICE_UP,
    LIMIT_PRICE_DOWN,
};
struct TradeData
{
    //排序值
@@ -24,7 +31,7 @@
    //价格
    string price;
    //是否为涨停价
    bool limitPrice;
    LIMIT_PRICE limitPrice;
    //手数
    int num;
   //操作类型 
ConsoleApplication/RecognitionUtil.h
@@ -11,15 +11,15 @@
enum OperateType {
    //买
    BUY,
    OPERATE_BUY,
    //买撤
    BUY_CANCEL,
    OPERATE_BUY_CANCEL,
    //卖
    SELL,
    OPERATE_SELL,
    //卖撤
    SELL_CANCEL,
    OPERATE_SELL_CANCEL,
    //错误
    OPERATE_ERROR,
    OPERATE_OPERATE_ERROR,
};
class NumberData {
ConsoleApplication/ScreenDataCapture.cpp
@@ -1,14 +1,127 @@
#include "ScreenDataCapture.h"
#include <thread>
#include<fstream>
#include "THSActionUtil.h"
#include <thread>
bool ScreenDataCapture::inited;
 OpenCLExcuter* ScreenDataCapture::openCLExcuter[THS_FRAME_COUNT];
//是否正在执行
 bool ScreenDataCapture::running;
 bool ScreenDataCapture::runnings[THS_FRAME_COUNT];
 clock_t ScreenDataCapture::latest_running_times[THS_FRAME_COUNT];
 CallbackFun ScreenDataCapture::data_callback;
 string ScreenDataCapture::gpCodes[THS_FRAME_COUNT];
 void* ScreenDataCapture::context;
//运行
void ScreenDataCapture::_run(int index)
{
    while (true) {
        if (running && runnings[index]&& gpCodes[index].length()>0) {
            latest_running_times[index] = clock();
            //识别数据
            string code = gpCodes[index];
            list<TradeData> resultList=captureLevel2TradeData(CaptureUtil::capture(index), index);
            data_callback(index,code, resultList, context);
        }
        Sleep(2);
    }
}
void ScreenDataCapture::setGPCode(int index, string code) {
    int length = sizeof(gpCodes) / sizeof(gpCodes[0]);
    if (length <= index) {
        return;
    }
    gpCodes[index] = code;
}
string ScreenDataCapture::getGPCode(int index) {
    return    gpCodes[index];
}
static string getGPCode(int index);
ScreenDataCapture::ScreenDataCapture() {
    openCLExcuter = new OpenCLExcuter();
    openCLExcuter->init();
}
void ScreenDataCapture::refreshHWND() {
    HWND win = THSActionUtil::getL2Win();
    if (win <= 0) {
        throw string("未获取到同花顺LEVEL2盘口");
    }
    CaptureUtil::init(win);
}
void ScreenDataCapture::init(CallbackFun callback, void* contex) {
    inited = true;
    data_callback = callback;
    context = contex;
    for (int i = 0;i < THS_FRAME_COUNT;i++)
    {
        openCLExcuter[i] = new OpenCLExcuter();
        openCLExcuter[i]->init();
    }
    //获取同花顺窗口句柄
    try {
        refreshHWND();
    }
    catch (string st) {
        throw st;
    }
    running = false;
    int length = sizeof(runnings) / sizeof(runnings[0]);
    for (int i = 0;i < length;i++) {
        runnings[i] = false;
        thread rt(&(ScreenDataCapture::_run),i);
        rt.detach();
    }
}
bool ScreenDataCapture::isRunning() {
    return running;
}
void ScreenDataCapture::start(int index) {
    runnings[index] = true;
}
//结束
void ScreenDataCapture::stop(int index) {
    runnings[index] = false;
}
//全部开始
void ScreenDataCapture::start() {
    running = true;
}
//全部结束
void ScreenDataCapture::stop() {
    running = false;
}
void ScreenDataCapture::startAll() {
    int length = sizeof(runnings) / sizeof(runnings[0]);
    for (int i = 0;i < length;i++) {
        runnings[i] = true;
    }
}
void ScreenDataCapture::stopAll() {
    int length = sizeof(runnings) / sizeof(runnings[0]);
    for (int i = 0;i < length;i++) {
        runnings[i] = false;
    }
}
bool ScreenDataCapture::isInited() {
    return inited;
}
list<TradeData>  ScreenDataCapture::captureLevel2TradeData(cv::Mat oimg, int identify) {
    if (oimg.rows == 0 || oimg.cols == 0) {
@@ -31,9 +144,16 @@
    //灰度化
    cv::Mat img = cv::Mat::zeros(oimg.rows, oimg.cols, CV_8UC1);//ImgUtil::grayImage(oimg);
    uchar* imgData = (uchar*)malloc(sizeof(uchar) * oimg.rows * oimg.cols);
    openCLExcuter->rgb2Gray(oimg.data, oimg.cols, oimg.rows, imgData);
    uchar* imgData = (uchar*)malloc(sizeof(uchar) * oimg.rows * oimg.cols);
    //
    if (oimg.channels() == 3)
    {
        openCLExcuter[identify]->rgb2Gray(oimg.data, oimg.cols, oimg.rows, imgData);
    }
    else {
        openCLExcuter[identify]->rgba2Gray(oimg.data, oimg.cols, oimg.rows, imgData);
    }
    img.data = imgData;
    oimg.release();
@@ -70,10 +190,11 @@
        rowDataOneLevel[index * 4 + 2] = indexs[3];
        rowDataOneLevel[index * 4 + 3] = indexs[2];
        index++;
        free(indexs);
    }
    int* rowSplitDataOneLevel = (int*)malloc(sizeof(int) * rowDataList.size() * 4 * 7);
    openCLExcuter->splitRowData(imgData, img.cols, img.rows, rowDataOneLevel, rowDataList.size(), rowSplitDataOneLevel);
    openCLExcuter[identify]->splitL2RowData(imgData, img.cols, img.rows, rowDataOneLevel, rowDataList.size(), rowSplitDataOneLevel);
    free(rowDataOneLevel);
@@ -146,7 +267,7 @@
    clock_t time_33 = clock();
    std::cout << "数据准备-0数据准备: threadid-" << std::this_thread::get_id() << " 耗时:" << time_33 - time_32 << endl;
    openCLExcuter->splitNum(imgData, img.cols, img.rows, pos, 3 * rowDataList.size(), zeroData, _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER, totalNumberData);
    openCLExcuter[identify]->splitL2Num(imgData, img.cols, img.rows, pos, 3 * rowDataList.size(), zeroData, _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER, totalNumberData);
    free(pos);
    free(zeroData);
@@ -167,7 +288,7 @@
    //准备模板数字
    uchar* templateNums = (unsigned char*)malloc(sizeof(unsigned char) * (_NUMBER_L2_HEIGHT * rowDataList.size()) * _NUMBER_L2_WIDTH * 10 * _NUMBER_L2_TOTAL_NUMBER);
    openCLExcuter->createNumberTemplates(rowDataList.size(), _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER, ImgUtil::numsOneLevel_level2 ,templateNums);
    openCLExcuter[identify]->createNumberTemplates(rowDataList.size(), _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER, ImgUtil::numsOneLevel_level2 ,templateNums);
    //ImgUtil::createTemplateNumData(data.size());
    clock_t time_4 = clock();
@@ -178,20 +299,66 @@
    //图像识别(除开数字的部分)
    RecognitionManager* recognitionManager = new RecognitionManager();
    list<TradeData> resultList;
    int* notNumberResult = (int*)malloc(sizeof(int)* rowDataList.size()*2);
    openCLExcuter[identify]->recognitionNotNum(img.data,img.cols,img.rows, rowSplitDataOneLevel,7, rowDataList.size(), notNumberResult);
    for (int i = 0;i < rowDataList.size();i++) {
        TradeData td = TradeData();
        switch (notNumberResult[i * 2])
        {
        case 0:
            td.limitPrice = LIMIT_PRICE_NORMAL;break;
        case 1:
            td.limitPrice = LIMIT_PRICE_UP;break;
        case 2:
            td.limitPrice = LIMIT_PRICE_DOWN;break;
        default:
            break;
        }
        switch (notNumberResult[i * 2 + 1])
        {
        case OPERATE_BUY:
            td.operateType = OPERATE_BUY;
            break;
        case OPERATE_BUY_CANCEL:
            td.operateType = OPERATE_BUY_CANCEL;
            break;
        case OPERATE_SELL:
            td.operateType = OPERATE_SELL;
            break;
        case OPERATE_SELL_CANCEL:
            td.operateType = OPERATE_SELL_CANCEL;
            break;
        case OPERATE_OPERATE_ERROR:
            td.operateType = OPERATE_OPERATE_ERROR;
            break;
        };
        resultList.push_back(td);
    }
    free(notNumberResult);
    /*
    try {
        resultList = (*recognitionManager).recognition(img, rowSplitDataOneLevel, rowDataList.size());
    }
    catch (...) {
        throw ERROR_CODE_RECOGNITION_FAIL;
    }
    */
    clock_t time_5 = clock();
    std::cout << "非数字数据识别完成: threadid-" << std::this_thread::get_id() << " 耗时:" << time_5 - time_4 << endl;
    //数字识别
    uchar** numberResult = openCLExcuter->recognition_numbers(totalNumberData, templateNums, rowDataList.size() * _NUMBER_L2_HEIGHT, _NUMBER_L2_WIDTH * 10 * _NUMBER_L2_TOTAL_NUMBER, _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER);
    uchar** numberResult = openCLExcuter[identify]->recognition_numbers(totalNumberData, templateNums, rowDataList.size() * _NUMBER_L2_HEIGHT, _NUMBER_L2_WIDTH * 10 * _NUMBER_L2_TOTAL_NUMBER, _NUMBER_L2_WIDTH, _NUMBER_L2_HEIGHT, _NUMBER_L2_TOTAL_NUMBER);
    //释放内存
    free(totalNumberData);
    free(templateNums);
@@ -233,13 +400,12 @@
    std::cout << "数字识别完成: threadid-" << std::this_thread::get_id() << " 耗时:" << time_6 - time_5 << endl;
    //释放内存
    //img.release();
    free(imgData);
    img.release();
    rowDataList.clear();
    list<int*>().swap(rowDataList);
    //free(imgData);
    free(rowSplitDataOneLevel);
    delete recognitionManager;
    std::cout << "-------结束任务: threadid-" << std::this_thread::get_id() << "  序号:" << identify << " 耗时:" << clock() - starttime << endl;
@@ -247,8 +413,8 @@
}
//捕获level2的盘口数据
list<TradeData> ScreenDataCapture::captureLevel2TradeData(int frameIndex) throw(int) {
    cv::Mat img = CaptureUtil::capture(frameIndex);
list<TradeData> ScreenDataCapture::captureLevel2TradeData(HWND hwnd, int index) throw(int) {
    cv::Mat img = CaptureUtil::capture(hwnd);
    /*
    string path1 = "E:\\temp\\";
@@ -261,5 +427,5 @@
    std::map<string, TradeData> map;
    */
    return captureLevel2TradeData(img, frameIndex);
    return captureLevel2TradeData(img, index);
}
ConsoleApplication/ScreenDataCapture.h
@@ -4,20 +4,62 @@
#include <map> 
#include "OpenCLExcuter.h"
#include "CaptureUtil.h"
#define THS_FRAME_COUNT 1
typedef void (*CallbackFun)(int index, string code, list<TradeData> dataList, void* contex);
class ScreenDataCapture
{
private:
    OpenCLExcuter* openCLExcuter;
    static OpenCLExcuter* openCLExcuter[THS_FRAME_COUNT];
    static bool inited;
    //是否正在执行
    static bool running;
    static bool runnings[THS_FRAME_COUNT];
    static clock_t latest_running_times[THS_FRAME_COUNT];
    static string gpCodes[THS_FRAME_COUNT];
    static CallbackFun data_callback;
    static void* context;
    static void _run(int index);
public:
    ScreenDataCapture();
    //捕获level2的盘口数据
    list<TradeData>  captureLevel2TradeData(int winIndex) throw(int);
    //初始化
    void init(CallbackFun callback, void* context);
    list<TradeData>  captureLevel2TradeData(cv::Mat oimg,int identify);
    void refreshHWND();
    
    bool isRunning();
    //开始
    void start(int index);
    //结束
    void stop(int index);
    //全部开始
    void start();
    //全部结束
    void stop();
    //全部开始
    void startAll();
    //全部结束
    void stopAll();
    bool isInited();
    //捕获level2的盘口数据
    static list<TradeData>  captureLevel2TradeData(HWND hwnd,int index) throw(int);
    static list<TradeData>  captureLevel2TradeData(cv::Mat oimg,int identify);
    //设置代码
    static void setGPCode(int index, string code);
    //获取代码
    static string getGPCode(int index);
};
ConsoleApplication/THSActionUtil.cpp
@@ -2,6 +2,23 @@
#include "ImgDivider.h"
#include "Win32Util.h"
RecognitionManager* THSActionUtil::recognitionManager;
bool isMainScreen(string str) {
    if (str.find("同花顺(") != string::npos) {
        cout << str << endl;
        if (str.find("副屏") == string::npos) {
            if (str.find("热门股") != string::npos || str.find("我的板块") != string::npos) {
                return true;
            }
        }
    }
    return    false;
}
bool isSecondScreen(string str) {
    return    str.find("同花顺(") != string::npos && str.find("副屏") != string::npos;
}
//获取副屏
HWND getSecondWindow() {
@@ -10,12 +27,59 @@
    for (ele = wlist.begin();ele != wlist.end();ele++) {
        HWND hwnd = *ele;
        string str = Win32Util::getWindowName(hwnd);
        if (str.find("同花顺") != string::npos && str.find("副屏") != string::npos)
        if (isSecondScreen(str))
        {
            return hwnd;
        }
    }
    return 0;
}
HWND THSActionUtil::getL2Win() {
    list<HWND> wlist = Win32Util::searchWindow("同花顺(");
    list<HWND>::iterator ele;
    for (ele = wlist.begin();ele != wlist.end();ele++) {
        HWND hwnd = *ele;
        string str = Win32Util::getWindowName(hwnd);
        if (isMainScreen(str)) {
            return hwnd;
        }
    }
    return 0;
}
bool THSActionUtil::checkEnv() {
    list<HWND> wlist = Win32Util::searchWindow("同花顺(");
    list<HWND>::iterator ele;
    bool fp = false;
    bool zp = false;
    for (ele = wlist.begin();ele != wlist.end();ele++) {
        HWND hwnd = *ele;
        string str = Win32Util::getWindowName(hwnd);
        if (isSecondScreen(str))
        {
            fp = true;
        }
        if (isMainScreen(str)) {
            zp = true;
        }
    }
    if (!zp) {
        throw string("同花顺主屏L2窗口未打开");
    }
    if (!fp) {
        throw string("同花顺副屏没有打开");
    }
    return true;
}
void THSActionUtil::openSecondScreen() {
@@ -29,7 +93,7 @@
    for (ele = wlist.begin();ele != wlist.end();ele++) {
        HWND hwnd = *ele;
        string str = Win32Util::getWindowName(hwnd);
        if (str.find("同花顺") != string::npos && str.find("副屏") != string::npos)
        if (isSecondScreen(str))
        {
            cout << str << endl;
            SetForegroundWindow(hwnd);
@@ -38,7 +102,7 @@
            break;
        }
        if (str.find("同花顺") != string::npos && str.find("热门股") != string::npos) {
        if (isMainScreen(str)) {
            cout << hwnd << endl;
            //获取尺寸
            RECT rc;
@@ -51,6 +115,10 @@
    }
    if (!open) {
        if (mainPage <= 0) {
            throw("未找到首页");
        }
        Win32Util::focus(mainPage);
        //查找主窗口的工具栏
        HWND tool = FindWindowExA(mainPage, NULL, "AfxControlBar100s", NULL);
@@ -72,7 +140,13 @@
    }
}
//添加股票
void THSActionUtil::setGP(std::string quickCode, list<std::string> codeList) {
bool THSActionUtil::setGP(std::string quickCode, list<std::string> codeList) {
    list<std::string> tempCodeList;
    for (std::list<string>::iterator e = codeList.begin();e != codeList.end();e++) {
        tempCodeList.push_back(*e);
    }
    //打开副屏
    HWND sw = getSecondWindow();
    if (sw <= 0)
@@ -84,32 +158,37 @@
    if (sw <= 0) {
        throw("未打开副屏");
    }
    Win32Util::focus(sw);
    //打开板块
    Win32Util::keyboardNum(quickCode);
    Win32Util::keyboardNum(quickCode, 100);
    Win32Util::keyboard(VK_RETURN, 200);
    Sleep(1000);
    Sleep(2000);
    //设置板块中的股票
    //获取板块内容句柄
    HWND content = FindWindowExA(sw, NULL, "AfxFrameOrView100s", NULL);
    cv::Mat img = CaptureUtil::capture(content);
    if (img.cols <= 0 || img.rows <= 0) {
        throw("板块截屏内容为空");
    cv::Mat oimg = CaptureUtil::capture(content);
    cout << "副屏截图:" << oimg.rows << "-" << oimg.cols << endl;
    if (oimg.cols <= 0 || oimg.rows <= 0) {
        throw string("板块截屏内容为空");
    }
    cv::Mat img = ImgUtil::grayImage(oimg);
    oimg.release();
    std::list<GPCodeArea>  areaList = recognitionGPArea(img);
    std::list<string> addList;
    std::list<GPCodeArea> delList;
    std::list<GPCodeArea> resultList = recognitionNum(img, areaList);
    for (std::list<GPCodeArea>::reverse_iterator ele= resultList.rbegin();ele != resultList.rend();++ele) {
    for (std::list<GPCodeArea>::reverse_iterator ele = resultList.rbegin();ele != resultList.rend();++ele) {
        bool contains = false;
        std::list<string>::iterator e;
        for (e = codeList.begin();e != codeList.end();e++) {
        for (e = tempCodeList.begin();e != tempCodeList.end();e++) {
            if (*e == (*ele).code) {
                contains = true;
                break;
@@ -121,33 +200,70 @@
        }
        else
        {
            codeList.remove((*ele).code);
            tempCodeList.remove((*ele).code);
        }
    }
    //-----先删除需要删除的
    //获取内容板块坐标
    RECT rect;
    GetWindowRect(content, &rect);
    for (std::list<GPCodeArea>::iterator ele = delList.begin();ele != delList.end();++ele) {
    RECT rect;
    GetWindowRect(sw, &rect);
    Win32Util::mouseMove(rect.left + 10, rect.top + 5, 1);
    Win32Util::click(10);
    for (std::list<GPCodeArea>::iterator ele = delList.begin();ele != delList.end();++ele) {
        RECT rect;
        GetWindowRect(content, &rect);
        Win32Util::focus(sw);
        int x = rect.left;
        int y = rect.top;
        x += (*ele).startx+5;
        y += (*ele).starty+5;
        //选中删除
        Win32Util::click(x,y,50);
        x += (*ele).startx + 5;
        y += (*ele).starty + 5;        //选中删除
        Win32Util::mouseMove(x, y, 50);
        Win32Util::click(10);
        Win32Util::keyboard(VK_DELETE, 50);
    }
    img.release();
    //----增加
    //截图,识别出增加按钮位置,点击增加,输入内容
    for (std::list<string>::iterator ele = tempCodeList.begin();ele != tempCodeList.end();++ele) {
        addGP(*ele);
        Sleep(100);
    }
    oimg = CaptureUtil::capture(content);
    if (oimg.cols <= 0 || oimg.rows <= 0) {
        throw("板块截屏内容为空");
    }
    img = ImgUtil::grayImage(oimg);
    areaList = recognitionGPArea(img);
    resultList = recognitionNum(img, areaList);
    for (std::list<GPCodeArea>::iterator ele = resultList.begin();ele != resultList.end();++ele) {
        bool contains = false;
        std::list<string>::iterator e;
        for (e = codeList.begin();e != codeList.end();e++) {
            if (*e == (*ele).code) {
                contains = true;
                break;
            }
        }
        if (contains) {
            codeList.remove((*ele).code);
        }
    }
    if (codeList.size() == 0) {
        return true;
    }
    return false;
}
void add(string code) {
void THSActionUtil::addGP(string code) {
    //打开副屏
    HWND sw = getSecondWindow();
    if (sw <= 0)
@@ -161,7 +277,8 @@
    }
    HWND content = FindWindowExA(sw, NULL, "AfxFrameOrView100s", NULL);
    cv::Mat img = CaptureUtil::capture(content);
    cv::Mat oimg = CaptureUtil::capture(content);
    cv::Mat img = ImgUtil::grayImage(oimg);
    if (img.cols <= 0 || img.rows <= 0) {
        throw("板块截屏内容为空");
    }
@@ -169,13 +286,18 @@
    for (std::list<GPCodeArea>::iterator ele = areaList.begin();ele != areaList.end();++ele) {
        GPCodeArea codeArea = *ele;
        if (codeArea.type == IMG_TYPE_ADD) {
            Win32Util::focus(sw);
            RECT rc;
            GetWindowRect(HWND(0x00161728), &rc);
            GetWindowRect(content, &rc);
            int y = rc.top + codeArea.starty + (codeArea.endy - codeArea.starty) / 2;
            int x = rc.left + codeArea.startx + (codeArea.endx - codeArea.startx) / 2;
            Win32Util::click(x, y, 50);
            Sleep(10);
            Win32Util::click(x, y, 50);
            Sleep(10);
            Win32Util::click(x, y, 50);
            //输入股票代码
            Win32Util::keyboardNum(code,1000);
            Win32Util::keyboardNum(code, 1000);
            Win32Util::keyboard(VK_RETURN, 1500);
            //关闭按钮
            Sleep(100);
@@ -186,56 +308,9 @@
    }
}
//识别股票代码
std::list<GPCodeArea>  THSActionUtil::recognitionGPArea(cv::Mat img) {
    //获取title分隔线
    int rows = img.rows;
    int cols = img.cols;
    int r;
    int contentStartRow = -1;
    for (r = 5;r < img.rows;r++) {
        if (ImgDivider::isRowFull(img, r, cols - 100, cols - 5, 2)) {
            contentStartRow = r;
        }
list<GPCodeArea> splitGPCodeArea(cv::Mat img, int start_row, int start_col, int end_row, int end_col) {
        if (contentStartRow > -1) {
            break;
        }
    }
    if (contentStartRow < 0) {
        throw("起始行分隔出错");
    }
    //分隔列
    int c = 0;
    int startC = -1;
    int endC = -1;
    for (c = 50;c < cols;c++) {
        if (ImgDivider::isColFull(img, c, contentStartRow + 5, contentStartRow + 100, 2)) {
            if (startC < 0) {
                startC = c;
            }
            else {
                if (c - startC < 20) {
                    startC = c;
                }
                else {
                    endC = c;
                    break;
                }
            }
        }
    }
    if (startC < 0 || endC < 0) {
        throw("内容框分隔出错");
    }
    //cv::imshow("内容", cv::Mat(img, cv::Rect(startC, contentStartRow, endC - startC, rows - contentStartRow)));
    std::list<GPCodeArea> resultList;
    startC += 1;
    endC -= 1;
    list<GPCodeArea> resultList;
    //分隔行内容
    int emptyStartRow = -1;
@@ -244,8 +319,8 @@
    int endf = -1;
    std::list<int*> dataItemList;
    for (int i = contentStartRow + 30;i < rows;i++) {
        bool empty = ImgDivider::isRowEmpty(img, i, startC, startC + 50, 1, 64) && ImgDivider::isRowEmpty(img, i, startC + (endC - startC) / 2 - 40, startC + (endC - startC) / 2 + 40, 1, 64);
    for (int i = start_row;i < end_row;i++) {
        bool empty = ImgDivider::isRowEmpty(img, i, start_col, start_col + 50, 1, _IMG_BINARY_THRESHOLD) && ImgDivider::isRowEmpty(img, i, start_col + (end_col - start_col) / 2 - 40, start_col + (end_col - start_col) / 2 + 40, 1, _IMG_BINARY_THRESHOLD);
        if (empty) {
            if (emptyStartRow < 0) {
                emptyStartRow = i;
@@ -264,9 +339,9 @@
                //内容坐标
                   //LogUtil::debug("内容的高度为:%d \n", endf - startf);
                int* dd = (int*)malloc(sizeof(int) * 4);
                *dd = startC;
                *dd = start_col;
                *(dd + 1) = startf;
                *(dd + 2) = endC;
                *(dd + 2) = end_col;
                *(dd + 3) = endf;
                //行数据高大于6才为有效的行高
                if (endf - startf > 6)
@@ -427,6 +502,62 @@
}
//识别股票代码
std::list<GPCodeArea>  THSActionUtil::recognitionGPArea(cv::Mat img) {
    //imwrite("C:\\Users\\Administrator\\Desktop\\ocr\\test.jpg", img);
    //获取title分隔线
    int rows = img.rows;
    int cols = img.cols;
    int r;
    int contentStartRow = -1;
    for (r = 5;r < img.rows;r++) {
        if (ImgDivider::isRowFull(img, r, cols - 100, cols - 5, 2)) {
            contentStartRow = r;
        }
        if (contentStartRow > -1) {
            break;
        }
    }
    if (contentStartRow < 0) {
        throw("起始行分隔出错");
    }
    //分隔列
    int c = 0;
    int startC = -1;
    int endC = -1;
    for (c = 50;c < cols;c++) {
        if (ImgDivider::isColFull(img, c, contentStartRow + 5, contentStartRow + 100, 2)) {
            if (startC < 0) {
                startC = c;
            }
            else {
                if (c - startC < 20) {
                    startC = c;
                }
                else {
                    endC = c;
                    break;
                }
            }
        }
    }
    if (startC < 0 || endC < 0) {
        throw("内容框分隔出错");
    }
    //cv::imshow("内容", cv::Mat(img, cv::Rect(startC, contentStartRow, endC - startC, rows - contentStartRow)));
    startC += 1;
    endC -= 1;
    std::list<GPCodeArea> resultList = splitGPCodeArea(img, contentStartRow + 30, startC, rows, endC);
    return resultList;
}
std::list<GPCodeArea>   THSActionUtil::recognitionNum(cv::Mat img, std::list<GPCodeArea> areaList) {
    if (!recognitionManager) {
@@ -437,11 +568,34 @@
    std::list<GPCodeArea>::iterator ele;
    int index = 0;
    for (ele = areaList.begin();ele != areaList.end();ele++) {
        index++;
        GPCodeArea codeArea = *ele;
        if (codeArea.type == IMG_TYPE_GP) {
            cv::Mat nums = cv::Mat(img, cv::Rect(codeArea.startx, codeArea.starty, codeArea.endx - codeArea.startx + 1, codeArea.endy - codeArea.starty + 1));
            std::list<cv::Mat> list2 = ImgUtil::splitNum(nums);
            //保存分隔的数据
            if (false) {
                string path = "C:\\Users\\Administrator\\Desktop\\ocr\\gpcode\\";
                path.append(to_string(index));
                path.append(".jpg");
                cv::imwrite(path, nums);
            }
            std::list<cv::Mat> list2 = ImgUtil::splitNum(nums, 96);
            if (false) {
                std::list<cv::Mat>::iterator e;
                int ci = 0;
                for (e = list2.begin();e != list2.end();e++) {
                    ci++;
                    string path = "C:\\Users\\Administrator\\Desktop\\ocr\\gpcode\\";
                    path.append(to_string(index)).append("_").append(to_string(ci));
                    path.append(".jpg");
                    cv::imwrite(path, *e);
                }
            }
            std::list<uchar> resultList = recognitionManager->recognitionGPCode(list2);
            std::list<uchar>::iterator ele1;
            string code = "";
@@ -449,9 +603,231 @@
                code.append(to_string(*ele1));
            }
            codeArea.code = code;
            cout << code << endl;
            codeList.push_back(codeArea);
        }
    }
    return codeList;
}
//分隔L2数据的目录
std::list<GPCodeArea> splitL2Cate(cv::Mat img) {
    int cols = img.cols;
    int contentStartRow = -1;
    int contentEndRow = -1;
    for (int r = 5;r < img.rows;r++) {
        if (ImgDivider::isRowFull(img, r, 10, 200, 2, 10, 30) && ImgDivider::isRowFull(img, r, img.cols - 200, img.cols - 1, 2, 10, 30)) {
            if (contentStartRow < 0)
            {
                contentStartRow = r;
            }
            else {
                if (r - contentStartRow > 10) {
                    contentEndRow = r;
                }
            }
        }
        if (contentStartRow > -1 && contentEndRow > -1) {
            break;
        }
    }
    if (contentStartRow < 0 || contentEndRow < 0) {
        throw("起始行或结束行分隔出错");
    }
    //分隔列
    list<int*> dataColIndexs;
    int startf = -1;
    int endf = -1;
    int startIndex = -1;
    for (int i = 10;i < cols;i++) {
        if (startIndex == -1) {
            startIndex = i;
        }
        bool full = ImgDivider::isColFull(img, i, contentStartRow, contentEndRow, 2);
        if (full) {
            if (startf < 0)
            {
                startf = i;
                endf = i;
            }
            else {
                endf = i;
            }
        }
        else {
            if (startf > -1 && endf > -1) {
                int width = endf - startf + 1;
                int* dd = (int*)malloc(sizeof(int) * 2);
                *dd = startIndex;
                *(dd + 1) = startf - 1;
                dataColIndexs.push_back(dd);
                startIndex = i;
            }
            startf = -1;
            endf = -1;
        }
    }
    if (startf > -1 && endf > -1) {
        int width = endf - startf + 1;
        int* dd = (int*)malloc(sizeof(int) * 2);
        *dd = startIndex;
        *(dd + 1) = startf - 1;
        LogUtil::debug("列数据:%d-%d\n", *dd, *(dd + 1));
        dataColIndexs.push_back(dd);
        startf = -1;
        endf = -1;
    }
    if (cols - startIndex > 50) {
        int* dd = (int*)malloc(sizeof(int) * 2);
        *dd = startIndex;
        *(dd + 1) = cols - 1;
        dataColIndexs.push_back(dd);
    }
    std::list<GPCodeArea> areaList;
    list<int*>::iterator ele;
    for (ele = dataColIndexs.begin();ele != dataColIndexs.end();ele++) {
        int* p = *ele;
        int startx = *p;
        int endx = *(p + 1);
        GPCodeArea area = GPCodeArea();
        area.startx = startx;
        area.endx = endx;
        area.starty = contentStartRow;
        area.endy = contentEndRow;
        areaList.push_back(area);
    }
    return areaList;
}
std::map<int,string> THSActionUtil::getListenL2GPCodes() {
    std::list<GPCodeArea> areaList = getListenL2GP();
    std::map<int, string> map;
    for (std::list<GPCodeArea>::iterator ele = areaList.begin();ele != areaList.end();ele++) {
        GPCodeArea data = (*ele);
        if (map.count(data.cateIndex)==0&&data.type== IMG_TYPE_GP) {
            map[data.cateIndex] = data.code;
        }
    }
    return map;
}
std::list<GPCodeArea> THSActionUtil::getListenL2GP() {
    //截图当前有哪些股票代码
    list<HWND> wlist = Win32Util::searchWindow("同花顺(");
    HWND mainPage = 0;
    list<HWND>::iterator ele;
    for (ele = wlist.begin();ele != wlist.end();ele++) {
        HWND hwnd = *ele;
        string str = Win32Util::getWindowName(hwnd);
        if (isMainScreen(str)) {
            cout << hwnd << endl;
            //获取尺寸
            RECT rc;
            GetWindowRect(hwnd, &rc);
            if (rc.right - rc.left > 200 && rc.bottom - rc.top > 200) {
                mainPage = hwnd;
                break;
            }
        }
    }
    if (mainPage <= 0) {
        throw string("L2监听未打开");
    }
    HWND content = FindWindowExA(mainPage, NULL, "AfxFrameOrView100s", NULL);
    //截图
    cv::Mat oimg = CaptureUtil::capture(content);
    cv::Mat img = ImgUtil::grayImage(oimg);
    oimg.release();
    //分隔图片
    std::list<GPCodeArea>  areaList = splitL2Cate(img);
    int index = 0;
    for (std::list<GPCodeArea>::iterator ele = areaList.begin();ele != areaList.end();ele++) {
        GPCodeArea area = *ele;
        index++;
        std::list<GPCodeArea> resultList = splitGPCodeArea(img, area.starty + 20, area.startx, area.endy, area.endx);
        if (false) {
            std::string path = "C:\\Users\\Administrator\\Desktop\\ocr\\gpcode\\";
            path.append(std::to_string(index)).append("_listen.jpg");
            imwrite(path, cv::Mat(img, cv::Rect(area.startx, area.starty, area.endx - area.startx + 1, area.endy - area.starty + 1)));
        }
        for (std::list<GPCodeArea>::iterator ele1 = resultList.begin();ele1 != resultList.end();ele1++) {
            GPCodeArea areaC = *ele1;
            if (areaC.type == IMG_TYPE_GP) {
                list<cv::Mat> nums = ImgUtil::splitNum(cv::Mat(img, cv::Rect(areaC.startx, areaC.starty, areaC.endx - areaC.startx + 1, areaC.endy - areaC.starty + 1)), 96);
                if (!recognitionManager) {
                    recognitionManager = new RecognitionManager();
                }
                if (nums.size() != 6) {
                    throw string("代码分隔出错");
                }
                list<uchar>  rresult = recognitionManager->recognitionGPCode(nums);
                string num;
                for (std::list<uchar>::iterator e = rresult.begin();e != rresult.end();e++) {
                    num.append(to_string(*e));
                }
                (*ele1).code = num;
                (*ele1).cateIndex = index-1;
            }
        }
        return resultList;
    }
}
bool THSActionUtil::setListenL2GP(int p, string code) {
    std::list<GPCodeArea> resultList = getListenL2GP();
    HWND content = FindWindowExA(getL2Win(), NULL, "AfxFrameOrView100s", NULL);
    int index = 0;
    for (std::list<GPCodeArea>::iterator ele = resultList.begin();ele != resultList.end();ele++) {
        GPCodeArea result = *ele;
        if (result.code == code) {
            //点击
            RECT rc;
            GetWindowRect(content, &rc);
            int x = rc.left + result.startx + 5;
            int y = rc.top + result.starty + 5;
            Win32Util::focus(content);
            Win32Util::click(x, y, 50);
            return true;
        }
    }
    return false;
}
ConsoleApplication/THSActionUtil.h
@@ -21,18 +21,29 @@
    int endx;
    int endy;
    std::string code;
    //L2数据有效
    int cateIndex;
};
//同花顺事件
class THSActionUtil
{
private:
    static RecognitionManager *recognitionManager;
    static void addGP(string code);
    static std::list<GPCodeArea> getListenL2GP();
public:
    static HWND getL2Win();
    //检测环境
    static bool checkEnv();
    //打开同花顺的副屏幕
    static void openSecondScreen();
    //设置股票
    static void setGP(std::string quickCode, list<std::string> codeList);
    static bool setGP(std::string quickCode, list<std::string> codeList);
    //识别股票代码
    static std::list<GPCodeArea>  recognitionGPArea(cv::Mat img);
@@ -40,6 +51,17 @@
    static std::list<GPCodeArea>  recognitionNum(cv::Mat img,std::list<GPCodeArea> areaList);
    //设置获取level2的股票数据
    static bool setListenL2GP(int p,string code);
    //设置获取level2的股票代码
    static std::map<int, string> getListenL2GPCodes();
    
ConsoleApplication/Win32Util.cpp
@@ -61,7 +61,7 @@
 string Win32Util::getWindowName(HWND hwnd) {
     int length = GetWindowTextLength(hwnd);
     TCHAR getbuf[100];
     TCHAR getbuf[1000];
     GetWindowText(hwnd, getbuf, length + 1);
     int iLen = WideCharToMultiByte(CP_ACP, 0, getbuf, -1, NULL, 0, NULL, NULL);   //首先计算TCHAR 长度。
     char* chRtn = new char[iLen * sizeof(char)];  //定义一个 TCHAR 长度大小的 CHAR 类型。
ConsoleApplication/kernel.cl
@@ -1,10 +1,10 @@
__kernel void recognition_numbers_1(__global const unsigned char* a_in,
    __global const unsigned char* b_in, int width,int num_width,int num_height,int num_count,
    __global const unsigned char* b_in, int width, int num_width, int num_height, int num_count,
    __global unsigned char* result) {
    int p = get_global_id(0);
    int startIndex = p / width * width * num_width * num_height + p % (num_count*10) * num_width;
    int startIndex = p / width * width * num_width * num_height + p % (num_count * 10) * num_width;
    unsigned char t = 0;
    for (int r = 0;r < num_height;r++)
        for (int c = 0;c < num_width;c++) {
@@ -26,7 +26,7 @@
    int endIndex = (index + 1) * 10;
    //获取最小值
    int min =255;
    int min = 255;
    int minIndex = 11;
    for (int i = startIndex;i < endIndex;i++)
@@ -50,7 +50,7 @@
    return v >= 64 ? 1 : 0;
}
__kernel void split_num(__global const unsigned char* img_in, __global const int* pos_in, __global const unsigned char* zero, int width,int num_width,int num_height,int num_count,
__kernel void split_num(__global const unsigned char* img_in, __global const int* pos_in, __global const unsigned char* zero, int width, int num_width, int num_height, int num_count,
    __global unsigned char* result) {
    int index = get_global_id(0);
@@ -350,7 +350,7 @@
    //开始填充数据
    for (i = 0;i < maxNumberCount;i++) {
        unsigned char numData[100*100];
        unsigned char numData[100 * 100];
        if (fresult[i * 2] == -1) {
            //填充0
@@ -358,7 +358,7 @@
                for (int c = 0;c < num_width;c++) {
                    unsigned char value = get_binary_value(zero[r * num_width + c]);
                    //设置输出坐标的值 
                    numData[r* num_width+c] = value;
                    numData[r * num_width + c] = value;
                }
            }
@@ -373,10 +373,10 @@
                        //填充空白0    
                        value = get_binary_value(img_in[get_one_level_position(width, _startx + c, r)]);
                        //设置输出坐标的值 
                        numData[(r - starty)* num_width+c] = value;
                        numData[(r - starty) * num_width + c] = value;
                    }
                    else {
                        numData[(r - starty)* num_width+c] = 0;
                        numData[(r - starty) * num_width + c] = 0;
                    }
                }
            }
@@ -404,7 +404,7 @@
                for (int c = 0;c < num_width;c++) {
                    int findex = index_0 + index_1 + index_2 + index_3 + c;
                    //printf("index:%d-findex:%d \n",index, findex);
                    result[findex] = numData[r* num_width+c];
                    result[findex] = numData[r * num_width + c];
                }
            }
        }
@@ -417,7 +417,7 @@
__kernel void createTemplateNumbers(__global unsigned char* numbers,int num_width,int num_height, int countPerLine,
__kernel void createTemplateNumbers(__global unsigned char* numbers, int num_width, int num_height, int countPerLine,
    __global unsigned char* result) {
    int gid = get_global_id(0);
@@ -587,16 +587,163 @@
__kernel void rgb2GrayImg(__global unsigned char* imgs, int width,
    __global unsigned char* result) {
    int rowIndex= get_global_id(0);
    int rowIndex = get_global_id(0);
    for (int c = 0;c < width;c++) {
        int index = rowIndex * width + c;
        int start = index * 3;
        unsigned char R = imgs[start];
        unsigned  G = imgs[start + 1];
        unsigned  B = imgs[start + 2];
        unsigned char G = imgs[start + 1];
        unsigned char B = imgs[start + 2];
        result[index] = (76 * R + 150 * G + 30 * B) >> 8;
    }
}
__kernel void rgba2GrayImg(__global unsigned char* imgs, int width,
    __global unsigned char* result) {
    int rowIndex = get_global_id(0);
    for (int c = 0;c < width;c++) {
        int index = rowIndex * width + c;
        int start = index * 4;
        unsigned char R = imgs[start];
        unsigned char G = imgs[start + 1];
        unsigned char B = imgs[start + 2];
        result[index] = (76 * R + 150 * G + 30 * B) >> 8;
    }
}
//L2非数字识别
__kernel void recognition_not_num(__global unsigned char* imgs, __global int* rowIndexs, int width,
    __global int* result) {
    int index = get_global_id(0);
    int row = index / 2;
    int baseIndex = row * 4 * 7;
    if (index % 2 == 0) {
        //涨跌停价
        //获取数据坐标
        int startx = baseIndex + 3 * 4;
        int starty = startx + 1;
        int endx = startx + 2;
        int endy = startx + 3;
        if (rowIndexs[startx] <= 0 && rowIndexs[starty] <= 0 && rowIndexs[endx] <= 0 && rowIndexs[endy] <= 0) {
            result[row * 2] = 0;
        }
        else {
            startx = rowIndexs[startx];
            starty = rowIndexs[starty];
            endx = rowIndexs[endx];
            endy = rowIndexs[endy];
            result[row * 2] = 1;
            //去除上下的空白
            for (int r = starty;r <= endy;r++)
            {
                bool empty = true;
                for (int c = startx;c <= endx;c++) {
                    unsigned char value = imgs[get_one_level_position(width, c, r)];
                    if (get_binary_value(value)) {
                        empty = 0;
                        break;
                    }
                }
                if (!empty) {
                    starty = r;
                    break;
                }
            }
            for (int r = endy;r >= starty;r--)
            {
                bool empty = true;
                for (int c = startx;c <= endx;c++) {
                    unsigned char value = imgs[get_one_level_position(width, c, r)];
                    if (get_binary_value(value)) {
                        empty = 0;
                        break;
                    }
                }
                if (!empty) {
                    endy = r;
                    break;
                }
            }
            int my = (starty + endy) / 2;
            //计算上半部分的值
            int topValue = 0;
            for (int r = starty;r <= my;r++)
            {
                for (int c = startx;c <= endx;c++) {
                    unsigned char value = imgs[get_one_level_position(width, c, r)];
                    topValue += get_binary_value(value);
                }
            }
            //计算下半部分的值
            int bottomValue = 0;
            for (int r = my;r <= endy;r++)
            {
                for (int c = startx;c <= endx;c++) {
                    unsigned char value = imgs[get_one_level_position(width, c, r)];
                    bottomValue += get_binary_value(value);
                }
            }
            if (topValue > bottomValue) {
                //涨停
                result[row * 2] = 1;
            }
            else {
                //跌停
                result[row * 2] = 2;
            }
        }
    }
    else {
        //操作类型
            //计算值
        int startx = baseIndex + 6 * 4;
        int starty = startx + 1;
        int endx = startx + 2;
        int endy = startx + 3;
        startx = rowIndexs[startx];
        starty = rowIndexs[starty];
        endx = rowIndexs[endx];
        endy = rowIndexs[endy];
        //printf("%d: %d %d %d %d\n",row, startx, starty, endx, endy);
        unsigned int pixelCount = 0;
        for (int r = starty;r <= endy;r++)
        {
            for (int c = startx;c <= endx;c++) {
                unsigned char val = imgs[get_one_level_position(width, c, r)];
                pixelCount+=get_binary_value(val);
            }
        }
        //初始化错误复制
        int value = 4;
        if (abs(pixelCount - 39) < 5) {
            //买
            value = 0;
        }
        else if (abs(pixelCount - 51) < 5) {
            //卖
            value = 2;
        }
        else if (abs(pixelCount - 105) < 5) {
            //买撤
            value = 1;
        }
        else if (abs(pixelCount - 117) < 5) {
            //卖撤
            value = 3;
        }
        result[row * 2 + 1] = value;
    }
}
ConsoleApplication/main.cpp
@@ -20,7 +20,7 @@
        //同花顺(v9.10.50) - 深圳Level-2分时走势
        //同花顺(v9.10.50) - pppp
        list<TradeData>  map = creenDataCapture->captureLevel2TradeData(cv::imread( "C:\\Users\\Administrator\\Desktop\\ocr\\demo\\0_388.jpg"),p);
        break;
        //break;
    }
    return 0L;
@@ -90,12 +90,30 @@
}
*/
void notify(int index,string code, list<TradeData> data,void *context) {
    cout << index << endl;
}
int main() {
    //THSActionUtil::openSecondScreen();
    ImgUtil::init();
    ScreenDataCapture* capture = new ScreenDataCapture();
    capture->init(notify,nullptr);
    capture->start();
    capture->startAll();
    /*
    RecognitionUtil::init();
    CaptureUtil::init("同花顺(v9.10.50) - 热门股888");// 热门股888
    ImgUtil::init();
    ths();
    std::list<string> codeList;
    codeList.push_back("000333");
    bool setResult = THSActionUtil::setListenL2GP(4,"000333");
    printf("设置股票代码结果:%d\n", setResult);
        cv::waitKey(0);
    cv::destroyAllWindows();
    return 0;
    */
    /*
    RecognitionManager* recognitionManager = new RecognitionManager();
@@ -114,26 +132,9 @@
    }
    */
    cv::waitKey(0);
    cv::destroyAllWindows();
    return 0;
    string path = "C:\\Users\\Administrator\\Desktop\\ocr\\temp.jpg";
    cv::Mat img = CaptureUtil::capture(HWND(0x00010CA6));
    cv::imwrite(path, img);
    //return 0;
    /*
    
    RecognitionUtil::init();
    CaptureUtil::init("同花顺(v9.10.50) - 热门股888");// 热门股888
    ImgUtil::init();
    for (int i = 0;i < 1;i++)
    {
@@ -144,7 +145,8 @@
    
    system("PAUSE");
    */
    system("PAUSE");
    cv::waitKey(0);
    cv::destroyAllWindows();
@@ -153,7 +155,6 @@
int main__() {
    CaptureUtil::init("同花顺(v9.10.50) - 我的页面13");
    for (int i =5;i < 6;i++)
    {
     cv::Mat img=    CaptureUtil::capture(1);
app/ExcelUtil.cpp
@@ -42,9 +42,19 @@
        std::ostringstream out_str;
        out_str << setiosflags(ios::fixed) << std::setprecision(2) << stof(tradeData.price);
        ws->label(index, 0, tradeData.time, xf);
        ws->label(index, 1, out_str.str(), xf);
        ws->label(index, 2, tradeData.limitPrice ? L"涨停价" : L"非涨停价", xf);
        if (tradeData.limitPrice == LIMIT_PRICE_NORMAL) {
            ws->label(index, 2, L"正常价", xf);
        }
        else  if (tradeData.limitPrice == LIMIT_PRICE_UP) {
            ws->label(index, 2, L"涨停价", xf);
        }
        else  if (tradeData.limitPrice == LIMIT_PRICE_DOWN) {
            ws->label(index, 2, L"跌停价", xf);
        }
        ws->label(index, 3, to_string(tradeData.num), xf);
@@ -52,13 +62,13 @@
        wstring operate;
        switch (tradeData.operateType)
        {
        case BUY:
        case OPERATE_BUY:
            operate = L"买";break;
        case BUY_CANCEL:
        case OPERATE_BUY_CANCEL:
            operate = L"买撤";break;
        case SELL:
        case OPERATE_SELL:
            operate = L"卖";break;
        case SELL_CANCEL:
        case OPERATE_SELL_CANCEL:
            operate = L"卖撤";break;
        default:
            break;
app/JsonUtil.h
New file
@@ -0,0 +1,54 @@
#pragma once
#include <string>
#include <list>
#include "json/json.h"
#include <THSActionUtil.h>
Json::Value toJson(std::list<TradeData> dataList) {
    Json::Value root;
    std::list<TradeData>::iterator ele;
    int index = 0;
    for (ele = dataList.begin();ele != dataList.end();ele++) {
        Json::Value  item;
        item["time"] = (*ele).time;
        item["price"] = (*ele).price;
        item["num"] = (*ele).num;
        item["limitPrice"] = (*ele).limitPrice;
        item["operateType"] = (*ele).operateType;
        root[index++] = item;
    }
    return root;
}
//json对象转为字符串
std::string toJsonStr(Json::Value json) {
    Json::StreamWriterBuilder writerBuilder;
    //不自动换行
    writerBuilder.settings_["indentation"] = "";
    std::ostringstream os;
    std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
    jsonWriter->write(json, &os);
    string jsonStr = os.str();
    return jsonStr;
}
std::string loadData(int clientID, int channel,string code, std::list<TradeData> dataList) {
    Json::Value data;
    data["channel"] = channel;
    data["code"] = code;
    data["data"] = toJson(dataList);
    Json::Value root;
    root["type"] = 0;
    root["client"] = clientID;
    root["data"] = data;
    return toJsonStr(root);
}
Json::Value parseJson(string data) {
    Json::Value root;
    Json::Reader reader;
    reader.parse(data, root);
    return root;
}
app/NetWorkUtil.cpp
New file
@@ -0,0 +1,19 @@
#include "pch.h"
#include "NetWorkUtil.h"
#include <windows.h>
#include <winsock.h>
#include <string>
using namespace std;
#pragma comment(lib,"wsock32.lib")
int NetWorkUtil::get_localhost_ip(char* buff) {
    struct hostent* ph = 0;
    WSADATA w;
    WSAStartup(0x0101, &w);//这一行必须在使用任何SOCKET函数前写!
    gethostname(buff, 256);
    string hostNmae = buff;//此处获得本机名称
    ph = gethostbyname(buff);
    const char* IP = inet_ntoa(*((struct in_addr*)ph->h_addr_list[0]));//此处获得本机IP
    WSACleanup();
    return 0;
}
app/NetWorkUtil.h
New file
@@ -0,0 +1,10 @@
#pragma once
class NetWorkUtil
{
public:
    static int get_localhost_ip(char* buff);
};
app/SocketManager.cpp
@@ -1,24 +1,119 @@
#include "pch.h"
#include "SocketManager.h"
#define CHANNEL_NUM 9
#include <thread>
SocketManager::SocketManager() {
    init();
SOCKET SocketManager::socketServer;
ActionCallback SocketManager::actionCallback;
void* SocketManager::callbackContext;
SocketManager::SocketManager(ActionCallback callback, void* context) {
    init(callback,context);
}
SocketManager::~SocketManager() {
}
void SocketManager::init() {
void SocketManager::processMsg(SOCKET  client, void* context) {
    while (true)
    {
        //一直读取消息
        char recvBuff[1024];
        memset(recvBuff, 0, sizeof(recvBuff));
        int recv_len = recv(client, recvBuff, sizeof(recvBuff), 0);
        if (recv_len > 0)
        {
            cout << "接受到操作指令:" << recvBuff << endl;
            try {
                bool success = actionCallback(string(recvBuff), callbackContext);
                //发送操作结果
                if (success)
                {
                    char buff[] = "OK";
                    int iSend = send(client, buff, sizeof(buff), 0);
                    cout << "返回给客户端数据:" << buff << endl;
                }
                else {
                    char buff[] = "FAIL";
                    int iSend = send(client, buff, sizeof(buff), 0);
                    cout << "返回给客户端数据:" << buff << endl;
                }
            }
            catch (string st) {
                char buff[] = "FAIL";
                int iSend = send(client, buff, sizeof(buff), 0);
                cout << "设置采集代码出错:" << st << endl;
            }
        }
    }
}
void SocketManager::runSocketServer(void* context) {
    //创建server
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);//初始化
    socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    sockaddr_in sockaddr;
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    sockaddr.sin_port = htons(LOCAL_SERVER_PORT);
    int result;
    if (bind(socketServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) {
        closesocket(socketServer);
        WSACleanup();
        printf("绑定失败\n");
        return;
    }
    //在本地绑定端口
    result = listen(socketServer, SOMAXCONN);//开始监听
    SocketManager* sm = (SocketManager*)context;
    printf("wait for connection...\n");
    while (true)
    {
        int nsize = sizeof(SOCKADDR);
        SOCKADDR   clientAddr;
        SOCKET  client = accept(sm->socketServer, &clientAddr, &nsize);
        if (client == SOCKET_ERROR)
        {
            cout << "连接失败" << endl;
            continue;
        }
        thread msg(SocketManager::processMsg, client, context);
        msg.detach();
    }
    closesocket(socketServer);
    WSACleanup();
}
void SocketManager::init(ActionCallback callback, void* contex) {
    actionCallback = callback;
    callbackContext = contex;
    clientaddr.sin_family = AF_INET;
    clientaddr.sin_port = htons(PORT);
    clientaddr.sin_addr.S_un.S_addr = inet_addr(ADDR);
    //建立8个连接
    //建立9个连接,前8个为数据传输通道,后一个为命令传输通道
    sockClients.clear();
    for (int i = 0;i < 8;i++)
    for (int i = 0;i < CHANNEL_NUM;i++)
    {
        addClient(i);
    }
    Connect();
    thread server(SocketManager::runSocketServer, this);
    server.detach();
}
int SocketManager::getClientsNum() {
@@ -40,7 +135,7 @@
}
int SocketManager::Connect() {
    int scount=0;
    int scount = 0;
    list<SOCKET>::iterator ele;
    for (ele = sockClients.begin();ele != sockClients.end();++ele) {
        int i = connect(*ele, (sockaddr*)&clientaddr, sizeof(clientaddr));
@@ -73,7 +168,7 @@
}
BOOL SocketManager::sendMsg(int p, char* msg) {
BOOL SocketManager::sendMsg(int p, const char* msg) {
    SOCKET socket = getClient(p);
    char buffer[1024];
    send(socket, msg, strlen(msg), 0);
app/SocketManager.h
@@ -10,12 +10,21 @@
#include "framework.h"
#include <atlstr.h>
using namespace std;
#define LOCAL_SERVER_ADDR "127.0.0.1"
#define LOCAL_SERVER_PORT 9006
typedef bool (*ActionCallback)(string data, void* contex);
class SocketManager
{
private:
    list<SOCKET> sockClients;
    static SOCKET socketServer;
    static void runSocketServer(void* context);
    static void processMsg(SOCKET  client, void* context);
    const int PORT = 9001;
    const char* ADDR = "127.0.0.1";
    sockaddr_in clientaddr;
@@ -23,13 +32,14 @@
    //添加客户端
    void addClient(int p);
    static void* callbackContext;
    static ActionCallback actionCallback;
public:
    SocketManager();
    SocketManager(ActionCallback callback,void * context);
    ~SocketManager();
    void init();
    void init(ActionCallback callback, void* contex);
    int getClientsNum();
@@ -41,7 +51,7 @@
    void disConnect();
    int disConnect(int p);
    BOOL sendMsg(int,char *);
    BOOL sendMsg(int, const char *);
};
app/app.aps
Binary files differ
app/app.rc
Binary files differ
app/app.vcxproj
@@ -113,6 +113,9 @@
      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories>$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
    </ResourceCompile>
    <PostBuildEvent>
      <Command>editbin /SUBSYSTEM:CONSOLE $(OutDir)\$(ProjectName).exe</Command>
    </PostBuildEvent>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
@@ -193,6 +196,7 @@
    <ClInclude Include="BasicExcelVC6.hpp" />
    <ClInclude Include="ExcelUtil.h" />
    <ClInclude Include="GUITool.h" />
    <ClInclude Include="JsonUtil.h" />
    <ClInclude Include="level2DataDlg.h" />
    <ClInclude Include="appDlg.h" />
    <ClInclude Include="framework.h" />
@@ -206,6 +210,7 @@
    <ClInclude Include="lib_json\include\json\value.h" />
    <ClInclude Include="lib_json\include\json\version.h" />
    <ClInclude Include="lib_json\include\json\writer.h" />
    <ClInclude Include="NetWorkUtil.h" />
    <ClInclude Include="pch.h" />
    <ClInclude Include="Resource.h" />
    <ClInclude Include="SocketManager.h" />
@@ -221,6 +226,7 @@
    <ClCompile Include="json_writer.cpp" />
    <ClCompile Include="level2DataDlg.cpp" />
    <ClCompile Include="appDlg.cpp" />
    <ClCompile Include="NetWorkUtil.cpp" />
    <ClCompile Include="pch.cpp">
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
app/app.vcxproj.filters
@@ -84,6 +84,12 @@
    <ClInclude Include="lib_json\include\json\writer.h">
      <Filter>头文件\json</Filter>
    </ClInclude>
    <ClInclude Include="NetWorkUtil.h">
      <Filter>头文件</Filter>
    </ClInclude>
    <ClInclude Include="JsonUtil.h">
      <Filter>头文件</Filter>
    </ClInclude>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="app.cpp">
@@ -119,6 +125,9 @@
    <ClCompile Include="json_writer.cpp">
      <Filter>源文件\json</Filter>
    </ClCompile>
    <ClCompile Include="NetWorkUtil.cpp">
      <Filter>源文件</Filter>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="app.rc">
app/appDlg.cpp
@@ -8,6 +8,9 @@
#include "appDlg.h"
#include "afxdialogex.h"
#include "level2DataDlg.h"
#include "THSActionUtil.h"
#include "NetWorkUtil.h"
#include "JsonUtil.h"
#ifdef _DEBUG
#define new DEBUG_NEW
@@ -55,19 +58,15 @@
// CappDlg 对话框
CappDlg::CappDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_APP_DIALOG, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    socketManager = new SocketManager();
    socketManager = new SocketManager(CappDlg::OnActionCallback, this);
    RecognitionUtil::init();
    CaptureUtil::init("同花顺(v9.10.50) - 热门股888");// 热门股888
    ImgUtil::init();
    capture = new   ScreenDataCapture();
    capture = new  ScreenDataCapture();
}
void CappDlg::DoDataExchange(CDataExchange* pDX)
@@ -83,6 +82,8 @@
    ON_BN_CLICKED(IDC_BUTTON10, &CappDlg::OnBnClickedButton10)
    ON_BN_CLICKED(IDC_BUTTON9, &CappDlg::OnBnClickedButton9)
    ON_BN_CLICKED(IDC_BUTTON4, &CappDlg::OnBnClickedButton4)
    ON_BN_CLICKED(IDC_BUTTON20, &CappDlg::OnBnClickedButton20)
    ON_BN_CLICKED(IDC_BUTTON5, &CappDlg::OnBnClickedButton5)
END_MESSAGE_MAP()
@@ -118,6 +119,14 @@
    SetIcon(m_hIcon, FALSE);        // 设置小图标
    // TODO: 在此添加额外的初始化代码
    CString st;
    char ip[32];
    NetWorkUtil::get_localhost_ip(ip);
    CString ipStr(ip);
    st.Format(_T("本机IP:%s"), ipStr);
    GetDlgItem(IDC_STATIC3)->SetWindowTextW(st);
    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}
@@ -177,6 +186,69 @@
}
void  CappDlg::OnDataCallback(int index, string code, list<TradeData> dataList, void* context) {
    //转为json
    CappDlg* app = (CappDlg*)context;
    //cout << "回调:" << std::this_thread::get_id() << ":" << index << endl;
    CButton* btn = (CButton*)app->GetDlgItem(IDC_CHECK1);
    bool check = btn->GetCheck();
    if (check) {
        string data = loadData(0, index, code, dataList);
        app->socketManager->sendMsg(index, data.c_str());
    }
}
bool  CappDlg::OnActionCallback(string data, void* context) {
    //转为json
    CappDlg* app = (CappDlg*)context;
    cout << "action回调:" << std::this_thread::get_id() << ":" << data << endl;
    //解析命令
    Json::Value root = parseJson(data);
    if (root["action"].asString() == "setGPCode") {
        //设置股票代码
        int index = root["data"]["index"].asInt();
        string code = root["data"]["code"].asString();
        string quickCode = "5";
        quickCode.append(to_string(index + 1));
        list<string> codeList;
        codeList.push_back(code);
        try {
            if (!app->capture->isInited()) {
                throw string("采集器尚未初始化");
            }
            //暂停采集
            app->capture->stop(index);
            bool result = THSActionUtil::setGP(quickCode, codeList);
            if (!result) {
                throw string("设置版块监控的GP失败");
            }
            THSActionUtil::setListenL2GP(index, code);
            map<int, string> results = THSActionUtil::getListenL2GPCodes();
            //设置代码
            for (map<int, string>::iterator ele = results.begin();ele != results.end();ele++) {
                int key = (*ele).first;
                string value = (*ele).second;
                app->capture->setGPCode(key, value);
            }
        }
        catch (string e) {
            CString msg(e.c_str());
            AfxMessageBox(msg);
            throw e;
        }
        //恢复采集
        app->capture->start(index);
    }
    return true;
}
void CappDlg::OnBnClickedButton1()
{
@@ -231,7 +303,13 @@
void CappDlg::OnBnClickedButton10()
{
    socketManager->disConnect();
    socketManager->init();
    try {
        socketManager->init(CappDlg::OnActionCallback, this);
    }
    catch (string st) {
        CString msg(st.c_str());
        AfxMessageBox(msg);
    }
    int scount = 0;
    int num = socketManager->getClientsNum();
    for (int i = 0; i < num; i++)
@@ -250,7 +328,16 @@
//识别测试
void CappDlg::OnBnClickedButton9()
{
    if (!capture->isInited())
    {
        try {
            capture->init(OnDataCallback, this);
        }
        catch (string st) {
            CString msg(st.c_str());
            AfxMessageBox(msg);
        }
    }
    clock_t time = clock();
    list<TradeData> result = capture->captureLevel2TradeData(cv::imread("C:\\Users\\Administrator\\Desktop\\ocr\\new.jpg"), 0);
    time = clock() - time;
@@ -270,7 +357,7 @@
        HWND hwndPointNow = NULL;
        hwndPointNow =(HWND) WindowFromPoint(pNow);  // 获取鼠标所在窗口的句柄
        hwndPointNow = (HWND)WindowFromPoint(pNow);  // 获取鼠标所在窗口的句柄
        if (hwndPointNow)
@@ -287,3 +374,78 @@
}
//环境检测
void CappDlg::OnBnClickedButton20()
{
    try {
        THSActionUtil::checkEnv();
        AfxMessageBox(_T("检测通过"));
    }
    catch (const string& st) {
        CString msg(st.c_str());
        AfxMessageBox(msg);
    }
}
//启动任务
void CappDlg::OnBnClickedButton5()
{
    //检测环境
    try {
        THSActionUtil::checkEnv();
    }
    catch (string st) {
        CString msg(st.c_str());
        AfxMessageBox(msg);
        return;
    }
    CButton* btn = (CButton*)GetDlgItem(IDC_BUTTON5);
    if (!capture->isInited())
    {
        try {
            capture->init(OnDataCallback, this);
        }
        catch (string st) {
            CString msg(st.c_str());
            AfxMessageBox(msg);
            return;
        }
    }
    if (!capture->isRunning()) {
        try {
            map<int, string> result = THSActionUtil::getListenL2GPCodes();
            //设置代码
            for (map<int, string>::iterator ele = result.begin();ele != result.end();ele++) {
                int key= (*ele).first;
                string value = (*ele).second;
                capture->setGPCode(key,value);
            }
        }
        catch (string st) {
            CString msg(st.c_str());
            AfxMessageBox(msg);
            return;
        }
        //刷新窗口句柄
        capture->refreshHWND();
        //句柄初始化
        capture->start();
        capture->startAll();
        btn->SetWindowText(_T("暂停识别任务"));
    }
    else {
        capture->stop();
        capture->stopAll();
        btn->SetWindowText(_T("启动识别任务"));
    }
}
app/appDlg.h
@@ -7,6 +7,9 @@
#include "ScreenDataCapture.h"
#include "CaptureUtil.h"
// CappDlg 对话框
class CappDlg : public CDialogEx
{
@@ -16,6 +19,9 @@
private:
    SocketManager *socketManager;
    ScreenDataCapture* capture;
    void OnDataCallback(int index, list<TradeData> dataList);
    static void  OnDataCallback(int index,string code, list<TradeData> dataList, void* context);
    static bool  OnActionCallback(string data, void* context);
// 构造
@@ -51,4 +57,6 @@
    afx_msg void OnBnClickedButton10();
    afx_msg void OnBnClickedButton9();
    afx_msg void OnBnClickedButton4();
    afx_msg void OnBnClickedButton20();
    afx_msg void OnBnClickedButton5();
};
app/kernel.cl
@@ -1,45 +1,45 @@
#define ROWS 8
#define COLS 5
__kernel void createBuffer(__global const float* a_in,
    __global const float* b_in,
    __global float* result) {
    int gid = get_global_id(0);
    result[gid] = a_in[gid] * b_in[gid];
}
__kernel void test1(__global const unsigned char* a_in,
    __global const unsigned char* b_in, int width,
__kernel void recognition_numbers_1(__global const unsigned char* a_in,
    __global const unsigned char* b_in, int width, int num_width, int num_height, int num_count,
    __global unsigned char* result) {
    int p = get_global_id(0);
    int startIndex = p / width * width * 5 * 8 + p % 170 * 5;
    int startIndex = p / width * width * num_width * num_height + p % (num_count * 10) * num_width;
    unsigned char t = 0;
    for (int r = 0;r < 8;r++)
        for (int c = 0;c < 5;c++) {
            int index = startIndex + 5 * width * r + c;
            t += (a_in[index] ^ b_in[index]);
    for (int r = 0;r < num_height;r++)
        for (int c = 0;c < num_width;c++) {
            int index = startIndex + num_width * width * r + c;
            t += abs(a_in[index] - b_in[index]);
            if (p == 0) {
                //printf("ֵ:%d-%d \n", a_in[index], b_in[index]);
            }
        }
    result[p] = t;
}
__kernel void test2(__global const unsigned char* a_in,
__kernel void recognition_numbers_2(__global const unsigned char* a_in,
    __global unsigned char* result) {
    int index = get_global_id(0);
    int startIndex = index * 10;
    int endIndex = (index + 1) * 10;
    //获取最小值
    int min = 255;
    int minIndex = 11;
    for (int i = startIndex;i < endIndex;i++)
    {
        if (a_in[i] == 0) {
            result[index] = i - startIndex;
            break;
        if (a_in[i] < min) {
            min = a_in[i];
            minIndex = i;
        }
    }
    //printf("最小值:%d - %d - %d \n", index, min, minIndex - startIndex);
    result[index] = minIndex - startIndex;
}
int get_one_level_position(int width, int x, int y) {
@@ -49,13 +49,8 @@
unsigned char get_binary_value(unsigned char v) {
    return v >= 64 ? 1 : 0;
}
__kernel void test5(__global const unsigned char* img_in, __global const int* pos_in, __global const unsigned char* zero, int width,
    __global unsigned char* result) {
}
__kernel void test4(__global const unsigned char* img_in, __global const int* pos_in, __global const unsigned char* zero, int width,
__kernel void split_num(__global const unsigned char* img_in, __global const int* pos_in, __global const unsigned char* zero, int width, int num_width, int num_height, int num_count,
    __global unsigned char* result) {
    int index = get_global_id(0);
@@ -355,15 +350,15 @@
    //开始填充数据
    for (i = 0;i < maxNumberCount;i++) {
        unsigned char numData[8][5];
        unsigned char numData[100 * 100];
        if (fresult[i * 2] == -1) {
            //填充0
            for (int r = 0;r < 8;r++) {
                for (int c = 0;c < 5;c++) {
                    unsigned char value = get_binary_value(zero[r * 5 + c]);
            for (int r = 0;r < num_height;r++) {
                for (int c = 0;c < num_width;c++) {
                    unsigned char value = get_binary_value(zero[r * num_width + c]);
                    //设置输出坐标的值 
                    numData[r][c] = value;
                    numData[r * num_width + c] = value;
                }
            }
@@ -372,44 +367,44 @@
            int _startx = fresult[i * 2];
            int _endx = fresult[i * 2 + 1];
            for (int r = starty;r <= endy;r++) {
                for (int c = 0;c < 5;c++) {
                for (int c = 0;c < num_width;c++) {
                    unsigned char value = 0;
                    if (_startx + c <= _endx) {
                        //填充空白0    
                        value = get_binary_value(img_in[get_one_level_position(width, _startx + c, r)]);
                        //设置输出坐标的值 
                        numData[r - starty][c] = value;
                        numData[(r - starty) * num_width + c] = value;
                    }
                    else {
                        numData[r - starty][c] = 0;
                        numData[(r - starty) * num_width + c] = 0;
                    }
                }
            }
        }
        int rowData = ROWS * COLS * 10 * 17;
        int rowData = num_height * num_width * 10 * num_count;
        int rowIndex = index / 3;
        int index_0 = rowData * rowIndex;
        //设置坐标值
        int index_2 = 0;
        if (index % 3 == 0) {
            index_2 += COLS * 10 * i;
            index_2 += num_width * 10 * i;
        }
        else if (index % 3 == 1) {
            index_2 += (COLS * 10) * (6 + i);
            index_2 += (num_width * 10) * (6 + i);
        }
        else {
            index_2 += (COLS * 10) * (6 + 6 + i);
            index_2 += (num_width * 10) * (6 + 6 + i);
        }
        for (int re = 0;re < 10;re++) {
            int index_3 = re * COLS;
            for (int r = 0;r < 8;r++) {
                int index_1 = r * (COLS * 10 * 17);
                for (int c = 0;c < COLS;c++) {
            int index_3 = re * num_width;
            for (int r = 0;r < num_height;r++) {
                int index_1 = r * (num_width * 10 * num_count);
                for (int c = 0;c < num_width;c++) {
                    int findex = index_0 + index_1 + index_2 + index_3 + c;
                    //printf("index:%d-findex:%d \n",index, findex);
                    result[findex] = numData[r][c];
                    result[findex] = numData[r * num_width + c];
                }
            }
        }
@@ -422,30 +417,30 @@
__kernel void createTemplateNumbers(__global unsigned char* numbers, int countPerLine,
__kernel void createTemplateNumbers(__global unsigned char* numbers, int num_width, int num_height, int countPerLine,
    __global unsigned char* result) {
    int gid = get_global_id(0);
    int LINE_NUMBER_COUNT = countPerLine;
    int NUMBER_COUNT = 10;
    //unsigned char* data = (unsigned char*)malloc(sizeof(unsigned char) * (ROWS * lines) *COLS * NUMBER_COUNT * LINE_NUMBER_COUNT);
    int outLineDataCount = ROWS * COLS * NUMBER_COUNT * LINE_NUMBER_COUNT;
    int inLineDataCount = COLS * NUMBER_COUNT * LINE_NUMBER_COUNT;
    int outLineDataCount = num_height * num_width * NUMBER_COUNT * LINE_NUMBER_COUNT;
    int inLineDataCount = num_width * NUMBER_COUNT * LINE_NUMBER_COUNT;
    int outLineCount = outLineDataCount * gid;
    for (int re = 0;re < LINE_NUMBER_COUNT;re++) {
        for (int n = 0;n < NUMBER_COUNT;n++)
        {
            int index_num_0 = n * ROWS * COLS;
            for (int r = 0;r < ROWS;r++) {
            int index_num_0 = n * num_width * num_height;
            for (int r = 0;r < num_height;r++) {
                int intLineCount = inLineDataCount * r;
                for (int c = 0;c < COLS;c++) {
                    int index_num = index_num_0 + r * COLS + c;
                for (int c = 0;c < num_width;c++) {
                    int index_num = index_num_0 + r * num_width + c;
                    unsigned char value = numbers[index_num];
                    int index = outLineCount;
                    index += intLineCount;
                    int x = re * NUMBER_COUNT * COLS + n * COLS + c;
                    int x = re * NUMBER_COUNT * num_width + n * num_width + c;
                    index += x;
                    result[index] = get_binary_value(value);
                }
@@ -592,16 +587,163 @@
__kernel void rgb2GrayImg(__global unsigned char* imgs, int width,
    __global unsigned char* result) {
    int rowIndex= get_global_id(0);
    int rowIndex = get_global_id(0);
    for (int c = 0;c < width;c++) {
        int index = rowIndex * width + c;
        int start = index * 3;
        unsigned char R = imgs[start];
        unsigned  G = imgs[start + 1];
        unsigned  B = imgs[start + 2];
        unsigned char G = imgs[start + 1];
        unsigned char B = imgs[start + 2];
        result[index] = (76 * R + 150 * G + 30 * B) >> 8;
    }
}
__kernel void rgba2GrayImg(__global unsigned char* imgs, int width,
    __global unsigned char* result) {
    int rowIndex = get_global_id(0);
    for (int c = 0;c < width;c++) {
        int index = rowIndex * width + c;
        int start = index * 4;
        unsigned char R = imgs[start];
        unsigned char G = imgs[start + 1];
        unsigned char B = imgs[start + 2];
        result[index] = (76 * R + 150 * G + 30 * B) >> 8;
    }
}
//L2非数字识别
__kernel void recognition_not_num(__global unsigned char* imgs, __global int* rowIndexs, int width,
    __global int* result) {
    int index = get_global_id(0);
    int row = index / 2;
    int baseIndex = row * 4 * 7;
    if (index % 2 == 0) {
        //涨跌停价
        //获取数据坐标
        int startx = baseIndex + 3 * 4;
        int starty = startx + 1;
        int endx = startx + 2;
        int endy = startx + 3;
        if (rowIndexs[startx] <= 0 && rowIndexs[starty] <= 0 && rowIndexs[endx] <= 0 && rowIndexs[endy] <= 0) {
            result[row * 2] = 0;
        }
        else {
            startx = rowIndexs[startx];
            starty = rowIndexs[starty];
            endx = rowIndexs[endx];
            endy = rowIndexs[endy];
            result[row * 2] = 1;
            //去除上下的空白
            for (int r = starty;r <= endy;r++)
            {
                bool empty = true;
                for (int c = startx;c <= endx;c++) {
                    unsigned char value = imgs[get_one_level_position(width, c, r)];
                    if (get_binary_value(value)) {
                        empty = 0;
                        break;
                    }
                }
                if (!empty) {
                    starty = r;
                    break;
                }
            }
            for (int r = endy;r >= starty;r--)
            {
                bool empty = true;
                for (int c = startx;c <= endx;c++) {
                    unsigned char value = imgs[get_one_level_position(width, c, r)];
                    if (get_binary_value(value)) {
                        empty = 0;
                        break;
                    }
                }
                if (!empty) {
                    endy = r;
                    break;
                }
            }
            int my = (starty + endy) / 2;
            //计算上半部分的值
            int topValue = 0;
            for (int r = starty;r <= my;r++)
            {
                for (int c = startx;c <= endx;c++) {
                    unsigned char value = imgs[get_one_level_position(width, c, r)];
                    topValue += get_binary_value(value);
                }
            }
            //计算下半部分的值
            int bottomValue = 0;
            for (int r = my;r <= endy;r++)
            {
                for (int c = startx;c <= endx;c++) {
                    unsigned char value = imgs[get_one_level_position(width, c, r)];
                    bottomValue += get_binary_value(value);
                }
            }
            if (topValue > bottomValue) {
                //涨停
                result[row * 2] = 1;
            }
            else {
                //跌停
                result[row * 2] = 2;
            }
        }
    }
    else {
        //操作类型
            //计算值
        int startx = baseIndex + 6 * 4;
        int starty = startx + 1;
        int endx = startx + 2;
        int endy = startx + 3;
        startx = rowIndexs[startx];
        starty = rowIndexs[starty];
        endx = rowIndexs[endx];
        endy = rowIndexs[endy];
        //printf("%d: %d %d %d %d\n",row, startx, starty, endx, endy);
        unsigned int pixelCount = 0;
        for (int r = starty;r <= endy;r++)
        {
            for (int c = startx;c <= endx;c++) {
                unsigned char val = imgs[get_one_level_position(width, c, r)];
                pixelCount+=get_binary_value(val);
            }
        }
        //初始化错误复制
        int value = 4;
        if (abs(pixelCount - 39) < 5) {
            //买
            value = 0;
        }
        else if (abs(pixelCount - 51) < 5) {
            //卖
            value = 2;
        }
        else if (abs(pixelCount - 105) < 5) {
            //买撤
            value = 1;
        }
        else if (abs(pixelCount - 117) < 5) {
            //卖撤
            value = 3;
        }
        result[row * 2 + 1] = value;
    }
}
app/level2DataDlg.cpp
@@ -91,13 +91,13 @@
        CString operate;
        switch (data.operateType)
        {
        case BUY:
        case OPERATE_BUY:
            operate = _T("买");break;
        case BUY_CANCEL:
        case OPERATE_BUY_CANCEL:
            operate = _T("买撤");break;
        case SELL:
        case OPERATE_SELL:
            operate = _T("卖");break;
        case SELL_CANCEL:
        case OPERATE_SELL_CANCEL:
            operate = _T("卖撤");break;
        default:
            break;
app/resource.h
@@ -19,6 +19,8 @@
#define IDC_BUTTON10                    1011
#define IDC_LIST1                       1012
#define IDC_BUTTON1                     1013
#define IDC_BUTTON20                    1014
#define IDC_STATIC3                     1016
// Next default values for new objects
// 
@@ -26,7 +28,7 @@
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        132
#define _APS_NEXT_COMMAND_VALUE         32771
#define _APS_NEXT_CONTROL_VALUE         1014
#define _APS_NEXT_CONTROL_VALUE         1017
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif