#include "expertsmartpointsetwin.h"
#include "gutil/qgui.h"
#include "globalfunc.h"
#include "crc.h"
#include "waitingdlg.h"
#include <QStackedLayout>
#include <QLabel>
#include <QRadioButton>
#include <QPushButton>
#include <QButtonGroup>
#include <QHeaderView>
#include <QMessageBox>
#include <QLineEdit>
#include <QApplication>
#include <QFileDialog>
#include <QJsonArray>
#include <QJsonDocument>
#include <QDialogButtonBox>

struct ModUnitMap {
    byte UUID = ie(16);
    byte type = ipp(4);
    byte len = ipp(4);
    byte w = ipp(2);
    byte h = ipp(2);
    byte ex1 = ipp(4);
    byte chipType = ipp(2);
    byte decodeMode = ipp(2);
    byte ex3 = ipp(4);
    byte smartset = ipp(4);
    byte smartsetRes = ipp(4);
    byte 走点len = ipp(4);
    byte endFlag = ipp(4);
    byte end = fi;
} modUnitMap;
struct ModMap {
    byte start = ie(4);
    byte 保留 = ipp(2);
    byte len = ipp(2);
    byte 换行时间 = ipp(2);
    byte 换行时刻 = ipp(2);
    byte 最小OE宽度 = ipp(2);
    byte 放电时间 = ipp(2);
    byte GLK占空比 = fi++;
    byte 灰度等级 = fi++;
    byte 数据时钟相位向后偏 = fi++;
    byte 数据时钟相位向前偏 = fi++;
    byte 时钟分频系数 = fi++;
    byte 倍频数 = fi++;
    byte 帧率 = fi++;
    byte 校正类型 = fi++;
    byte 芯片通道数 = ipp(3);
    byte 模组类型数目 = ipp();
    byte Unit = ipp(modUnitMap.end);
    byte 保留2 = ipp(4);
    byte check = ipp(4);
    byte end = fi;
} modMap;

ExpertSmartPointSetWin::ExpertSmartPointSetWin(ExpertWin *expertWin) : BaseWin{expertWin}, expertWin(expertWin) {
    setWindowModality(Qt::WindowModal);
    setAttribute(Qt::WA_DeleteOnClose);
    setWindowTitle(tr("智能走点参数配置"));
    resize(900, 600);

    ModuleWidth = expertWin->mModule["ModuleWidth"].toInt();
    ModuleHeight = expertWin->mModule["ModuleHeight"].toInt();
    GroupNum = expertWin->mModule["GroupNum"].toInt();
    ScanNum = expertWin->mModule["ScanNum"].toInt();
    ChipType = expertWin->mModule["ChipType"].toStr();
    DecodeMode = expertWin->mModule["DecodeMode"].toStr();
    GroupMode = expertWin->mModule["GroupMode"].toStr();

    auto vBox = new VBox(center);
    vBox->setContentsMargins(0,0,0,0);
    vBox->setSpacing(3);
    vBox->addLayout(addBtns(new QHBoxLayout()));

    auto stack = new QStackedLayout(vBox);

    auto vv = new VBox(stack);
    vv->setContentsMargins(50, 50, 6, 6);

    auto lb = new QLabel(tr("基本参数"));
    gFont(lb, 16);
    auto bkBlue = lb->palette();
    bkBlue.setColor(QPalette::WindowText, QColor(0x5599ff));
    lb->setPalette(bkBlue);
    vv->addWidget(lb);
    vv->addSpacing(50);

    auto grid = new Grid(vv);
    grid->setVerticalSpacing(30);
    grid->setColumnStretch(0, 1);
    grid->setColumnStretch(3, 1);
    grid->setColumnStretch(6, 1);

    lb = new QLabel(tr("模组宽度: "));
    grid->addWidget(lb, 0, 1);

    fdModuleWidth = new QSpinBox;
    fdModuleWidth->setRange(0, 9999);
    fdModuleWidth->setValue(ModuleWidth);
    grid->addWidget(fdModuleWidth, 0, 2);

    lb = new QLabel(tr("模组高度: "));
    grid->addWidget(lb, 1, 1);

    fdModuleHeight = new QSpinBox;
    fdModuleHeight->setRange(0, 9999);
    fdModuleHeight->setValue(ModuleHeight);
    grid->addWidget(fdModuleHeight, 1, 2);

    lb = new QLabel(tr("数据组数: "));
    grid->addWidget(lb, 2, 1);

    fdGroupNum = new QSpinBox;
    fdGroupNum->setRange(0, 9999);
    fdGroupNum->setValue(GroupNum);
    grid->addWidget(fdGroupNum, 2, 2);


    {
        dlgChipType = new QDialog(this);
        dlgChipType->setWindowFlag(Qt::WindowMaximizeButtonHint);
        dlgChipType->setWindowTitle("选择芯片类型");
        dlgChipType->resize(800, 600);

        auto vBox = new VBox(dlgChipType);
        vBox->setContentsMargins(0,0,0,0);

        tableChipType = new Table(24, 10);
        tableChipType->verticalHeader()->setMinimumWidth(30); //added by alahover 20230822
        tableChipType->setColFit();
        tableChipType->setRowFit();
        tableChipType->setEditTriggers(QAbstractItemView::NoEditTriggers);
        tableChipType->setAlternatingRowColors(true);
        tableChipType->setSelectionMode(QAbstractItemView::SingleSelection);
        int rr = 0, cc = 0;
        tableChipType->setHeaderText(cc, "ICN系列");
        tableChipType->setText(rr++, cc, "ICN2026");
        tableChipType->setText(rr++, cc, "ICN2027");
        tableChipType->setText(rr++, cc, "ICN2028");
        tableChipType->setText(rr++, cc, "ICN2037");
        tableChipType->setText(rr++, cc, "ICN2038");
        tableChipType->setText(rr++, cc, "ICN2038S");
        tableChipType->setText(rr++, cc, "ICN2053");
        tableChipType->setText(rr++, cc, "ICN2058");
        tableChipType->setText(rr++, cc, "ICN2088");
        tableChipType->setText(rr++, cc, "ICND2045");
        tableChipType->setText(rr++, cc, "ICND2046");
        tableChipType->setText(rr++, cc, "ICND2047");
        tableChipType->setText(rr++, cc, "ICND2049");
        tableChipType->setText(rr++, cc, "ICND2055");
        tableChipType->setText(rr++, cc, "ICND2059");
        tableChipType->setText(rr++, cc, "ICND2065");
        tableChipType->setText(rr++, cc, "ICND2069");
        tableChipType->setText(rr++, cc, "ICND2076");
        tableChipType->setText(rr++, cc, "ICND2110");
        tableChipType->setText(rr++, cc, "ICND2112");
        tableChipType->setText(rr++, cc, "ICND2153");
        tableChipType->setText(rr++, cc, "ICND2153_three");
        tableChipType->setText(rr++, cc, "ICND2163");
        tableChipType->setText(rr++, cc, "ICND2200");
        rr = 0; cc++;
        tableChipType->setHeaderText(cc, "SM系列");
        tableChipType->setText(rr++, cc, "SM16017_NEW");
        tableChipType->setText(rr++, cc, "SM16017S");
        tableChipType->setText(rr++, cc, "SM16159S");
        tableChipType->setText(rr++, cc, "SM16169S");
        tableChipType->setText(rr++, cc, "SM16207S");
        tableChipType->setText(rr++, cc, "SM16218");
        tableChipType->setText(rr++, cc, "SM16227S");
        tableChipType->setText(rr++, cc, "SM16237DS");
        tableChipType->setText(rr++, cc, "SM16237S");
        tableChipType->setText(rr++, cc, "SM16259S");
        tableChipType->setText(rr++, cc, "SM16259S");
        tableChipType->setText(rr++, cc, "SM16369");
        tableChipType->setText(rr++, cc, "SM16380");
        tableChipType->setText(rr++, cc, "SM16388");
        tableChipType->setText(rr++, cc, "SM16389");
        tableChipType->setText(rr++, cc, "SM16509/16399");
        rr = 0; cc++;
        tableChipType->setHeaderText(cc, "LS系列");
        tableChipType->setText(rr++, cc, "LS9918S");
        tableChipType->setText(rr++, cc, "LS9919S");
        tableChipType->setText(rr++, cc, "LS9926S");
        tableChipType->setText(rr++, cc, "LS9929C");
        tableChipType->setText(rr++, cc, "LS9929S");
        tableChipType->setText(rr++, cc, "LS9930S");
        tableChipType->setText(rr++, cc, "LS9931S");
        tableChipType->setText(rr++, cc, "LS9935B");
        tableChipType->setText(rr++, cc, "LS9935S");
        tableChipType->setText(rr++, cc, "LS9936S");
        tableChipType->setText(rr++, cc, "LS9961S");
        rr = 0; cc++;
        tableChipType->setHeaderText(cc, "MBI系列");
        tableChipType->setText(rr++, cc, "MBI5041/5042");
        tableChipType->setText(rr++, cc, "MBI5051");
        tableChipType->setText(rr++, cc, "MBI5124");
        tableChipType->setText(rr++, cc, "MBI5151");
        tableChipType->setText(rr++, cc, "MBI5153");
        tableChipType->setText(rr++, cc, "MBI5155");
        tableChipType->setText(rr++, cc, "MBI5158");
        tableChipType->setText(rr++, cc, "MBI5252");
        tableChipType->setText(rr++, cc, "MBI5253");
        tableChipType->setText(rr++, cc, "MBI5253B");
        tableChipType->setText(rr++, cc, "MBI5254");
        tableChipType->setText(rr++, cc, "MBI5264");
        tableChipType->setText(rr++, cc, "MBI5268");
        tableChipType->setText(rr++, cc, "MBI5353");
        tableChipType->setText(rr++, cc, "MBI5353B");
        tableChipType->setText(rr++, cc, "MBI6322");
        tableChipType->setText(rr++, cc, "MBI6328");
        tableChipType->setText(rr++, cc, "MBI6334");
        rr = 0; cc++;
        tableChipType->setHeaderText(cc, "SUM系列");
        tableChipType->setText(rr++, cc, "SUM2017");
        tableChipType->setText(rr++, cc, "SUM2017T");
        tableChipType->setText(rr++, cc, "SUM2017TD");
        tableChipType->setText(rr++, cc, "SUM2028");
        tableChipType->setText(rr++, cc, "SUM2030");
        tableChipType->setText(rr++, cc, "SUM2030T");
        tableChipType->setText(rr++, cc, "SUM2032");
        tableChipType->setText(rr++, cc, "SUM2033");
        tableChipType->setText(rr++, cc, "SUM2035");
        tableChipType->setText(rr++, cc, "SUM2036");
        tableChipType->setText(rr++, cc, "SUM2037");
        tableChipType->setText(rr++, cc, "SUM2117");
        tableChipType->setText(rr++, cc, "SUM2130");
        tableChipType->setText(rr++, cc, "SUM2131");
        tableChipType->setText(rr++, cc, "SUM2135");
        tableChipType->setText(rr++, cc, "SUM6086");
        rr = 0; cc++;
        tableChipType->setHeaderText(cc, "FM系列");
        tableChipType->setText(rr++, cc, "FM6128");
        tableChipType->setText(rr++, cc, "FM6129");
        tableChipType->setText(rr++, cc, "FM6153");
        tableChipType->setText(rr++, cc, "FM6182");
        tableChipType->setText(rr++, cc, "FM6253");
        tableChipType->setText(rr++, cc, "FM6353");
        tableChipType->setText(rr++, cc, "FM6356");
        tableChipType->setText(rr++, cc, "FM6363");
        tableChipType->setText(rr++, cc, "FM6555");
        tableChipType->setText(rr++, cc, "FM6565/6569");
        tableChipType->setText(rr++, cc, "FM6565CE");
        rr = 0; cc++;
        tableChipType->setHeaderText(cc, "DP系列");
        tableChipType->setText(rr++, cc, "DP5135");
        tableChipType->setText(rr++, cc, "DP5220X");
        tableChipType->setText(rr++, cc, "DP5525");
        rr = 0; cc++;
        tableChipType->setHeaderText(cc, "MY系列");
        tableChipType->setText(rr++, cc, "MY9758");
        tableChipType->setText(rr++, cc, "MY9862");
        tableChipType->setText(rr++, cc, "MY9866");
        rr = 0; cc++;
        tableChipType->setHeaderText(cc, "LYD系列");
        tableChipType->setText(rr++, cc, "LYD6168B");
        tableChipType->setText(rr++, cc, "LYD6168C");
        tableChipType->setText(rr++, cc, "LYD6168D");
        tableChipType->setText(rr++, cc, "LYD6168E");
        tableChipType->setText(rr++, cc, "LYD6188");
        tableChipType->setText(rr++, cc, "LYD6188PC");
        rr = 0; cc++;
        tableChipType->setHeaderText(cc, "其他系列");
        tableChipType->setText(rr++, cc, "通用");
        tableChipType->setText(rr++, cc, "A5065");
        tableChipType->setText(rr++, cc, "AXS6018");
        tableChipType->setText(rr++, cc, "CFD135A");
        tableChipType->setText(rr++, cc, "CFD455A");
        tableChipType->setText(rr++, cc, "CNS7153");
        tableChipType->setText(rr++, cc, "CNS7253");
        tableChipType->setText(rr++, cc, "CNS7263");
        tableChipType->setText(rr++, cc, "CS2017");
        tableChipType->setText(rr++, cc, "CS2033");
        tableChipType->setText(rr++, cc, "D26188");
        tableChipType->setText(rr++, cc, "HX8055");
        tableChipType->setText(rr++, cc, "HX8864");
        tableChipType->setText(rr++, cc, "HX8896");
        tableChipType->setText(rr++, cc, "RT5965");
        tableChipType->setText(rr++, cc, "RT5967");
        tableChipType->setText(rr++, cc, "SCL8081A");
        tableChipType->setText(rr++, cc, "XM11202G");
        connect(tableChipType, &Table::cellDoubleClicked, dlgChipType, [=](int row, int column) {
            auto text = tableChipType->text(row, column);
            if(text.isEmpty()) return;
            fdChipType->setText(text);
            ChipTypeCode = (column<<8) | row;
            dlgChipType->accept();
        });
        vBox->addWidget(tableChipType);

        auto btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Close);
        connect(btnBox, &QDialogButtonBox::rejected, dlgChipType, &QDialog::reject);
        connect(btnBox, &QDialogButtonBox::accepted, dlgChipType, [=] {
            auto items = tableChipType->selectedItems();
            if(items.isEmpty()) {
                QMessageBox::warning(dlgChipType, "Warning", "请选择芯片类型");
                return;
            }
            fdChipType->setText(items[0]->text());
            auto ranges = tableChipType->selectedRanges();
            ChipTypeCode = (ranges[0].leftColumn()<<8) | ranges[0].topRow();
            dlgChipType->accept();
        });
        vBox->addWidget(btnBox);
    }
    {
        dlgDecodeMode = new QDialog(this);
        dlgDecodeMode->setWindowFlag(Qt::WindowMaximizeButtonHint);
        dlgDecodeMode->setWindowTitle("选择译码方式");
        dlgDecodeMode->resize(600, 400);

        auto vBox = new VBox(dlgDecodeMode);
        vBox->setContentsMargins(0,0,0,0);

        tableDecodeMode = new Table(9, 7);
        tableDecodeMode->verticalHeader()->setMinimumWidth(30); //added by alahover 20230822
        tableDecodeMode->setColFit();
        tableDecodeMode->setRowFit();
        tableDecodeMode->setEditTriggers(QAbstractItemView::NoEditTriggers);
        tableDecodeMode->setAlternatingRowColors(true);
        tableDecodeMode->setSelectionMode(QAbstractItemView::SingleSelection);
        int rr = 0, cc = 0;
        tableDecodeMode->setHeaderText(cc, "系列"+QString::number(cc+1));
        tableDecodeMode->setText(rr++, cc, "138译码");
        tableDecodeMode->setText(rr++, cc, "ICN2012");
        tableDecodeMode->setText(rr++, cc, "MW4958");
        tableDecodeMode->setText(rr++, cc, "SM5166P");
        tableDecodeMode->setText(rr++, cc, "TC7260");
        tableDecodeMode->setText(rr++, cc, "TC7258E");
        tableDecodeMode->setText(rr++, cc, "TC7258EN");
        tableDecodeMode->setText(rr++, cc, "HX6012");
        tableDecodeMode->setText(rr++, cc, "ICN2016");
        rr = 0; cc++;
        tableDecodeMode->setHeaderText(cc, "系列"+QString::number(cc+1));
        tableDecodeMode->setText(rr++, cc, "595译码");
        rr = 0; cc++;
        tableDecodeMode->setHeaderText(cc, "系列"+QString::number(cc+1));
        tableDecodeMode->setText(rr++, cc, "5958译码");
        tableDecodeMode->setText(rr++, cc, "5960译码");
        tableDecodeMode->setText(rr++, cc, "TC7558");
        tableDecodeMode->setText(rr++, cc, "RT5957");
        tableDecodeMode->setText(rr++, cc, "HX6058");
        tableDecodeMode->setText(rr++, cc, "HX6158H");
        tableDecodeMode->setText(rr++, cc, "MBI5988");
        rr = 0; cc++;
        tableDecodeMode->setHeaderText(cc, "系列"+QString::number(cc+1));
        tableDecodeMode->setText(rr++, cc, "直接输出 (低)");
        tableDecodeMode->setText(rr++, cc, "直接输出 (高)");
        tableDecodeMode->setText(rr++, cc, "静态无译码");
        rr = 0; cc++;
        tableDecodeMode->setHeaderText(cc, "系列"+QString::number(cc+1));
        tableDecodeMode->setText(rr++, cc, "ICND2018");
        tableDecodeMode->setText(rr++, cc, "ICND3018");
        tableDecodeMode->setText(rr++, cc, "ICND2019");
        tableDecodeMode->setText(rr++, cc, "TC7559");
        tableDecodeMode->setText(rr++, cc, "TC7519");
        tableDecodeMode->setText(rr++, cc, "SM5366");
        tableDecodeMode->setText(rr++, cc, "DP32020");
        tableDecodeMode->setText(rr++, cc, "DP7298A");
        tableDecodeMode->setText(rr++, cc, "SM5368");
        rr = 0; cc++;
        tableDecodeMode->setHeaderText(cc, "系列"+QString::number(cc+1));
        tableDecodeMode->setText(rr++, cc, "ICND2013");
        tableDecodeMode->setText(rr++, cc, "TC7262");
        tableDecodeMode->setText(rr++, cc, "ICND2017");
        tableDecodeMode->setText(rr++, cc, "DP7268E");
        tableDecodeMode->setText(rr++, cc, "DP32019");
        tableDecodeMode->setText(rr++, cc, "HX6013");
        rr = 0; cc++;
        tableDecodeMode->setHeaderText(cc, "系列"+QString::number(cc+1));
        tableDecodeMode->setText(rr++, cc, "SM5266P");
        tableDecodeMode->setText(rr++, cc, "FM7239");
        tableDecodeMode->setText(rr++, cc, "LS9736");
        tableDecodeMode->setText(rr++, cc, "D7266");
        tableDecodeMode->setText(rr++, cc, "CFD21338SPC");
        tableDecodeMode->setText(rr++, cc, "LS9737_1");
        connect(tableDecodeMode, &Table::cellDoubleClicked, dlgDecodeMode, [=](int row, int column) {
            auto text = tableDecodeMode->text(row, column);
            if(text.isEmpty()) return;
            fdDecodeMode->setText(text);
            DecodeModeCode = (column<<8) | row;
            dlgDecodeMode->accept();
        });
        vBox->addWidget(tableDecodeMode);

        auto btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Close);
        connect(btnBox, &QDialogButtonBox::rejected, dlgDecodeMode, &QDialog::reject);
        connect(btnBox, &QDialogButtonBox::accepted, dlgDecodeMode, [=] {
            auto items = tableDecodeMode->selectedItems();
            if(items.isEmpty()) {
                QMessageBox::warning(dlgDecodeMode, "Warning", "请选择译码方式");
                return;
            }
            fdDecodeMode->setText(items[0]->text());
            auto ranges = tableDecodeMode->selectedRanges();
            DecodeModeCode = (ranges[0].leftColumn()<<8) | ranges[0].topRow();
            dlgDecodeMode->accept();
        });
        vBox->addWidget(btnBox);
    }

    lb = new QLabel(tr("驱动芯片: "));
    grid->addWidget(lb, 0, 4);

    auto hhhh = new HBox;
    hhhh->setSpacing(0);

    fdChipType = new QLineEdit("ICND2153");
    fdChipType->setMinimumWidth(150);
    fdChipType->setReadOnly(true);
    hhhh->addWidget(fdChipType);

    auto def = fdChipType->text();
    QTableWidgetItem *item;
    if(! def.isEmpty()) for(int r=0; r<tableChipType->rowCount(); r++) for(int c=0; c<tableChipType->columnCount(); c++) if((item = tableChipType->item(r, c)) && item->text()==def) item->setSelected(true);

    auto btn = new QPushButton("...");
    btn->setMaximumWidth(30);
    connect(btn, &QPushButton::clicked, dlgChipType, &QDialog::exec);
    hhhh->addWidget(btn);
    grid->addLayout(hhhh, 0, 5);

    lb = new QLabel(tr("译码方式: "));
    grid->addWidget(lb, 1, 4);

    hhhh = new HBox;
    hhhh->setSpacing(0);

    fdDecodeMode = new QLineEdit(tr("138译码"));
    fdDecodeMode->setReadOnly(true);
    hhhh->addWidget(fdDecodeMode);

    def = fdDecodeMode->text();
    if(! def.isEmpty()) for(int r=0; r<tableDecodeMode->rowCount(); r++) for(int c=0; c<tableDecodeMode->columnCount(); c++) if((item = tableDecodeMode->item(r, c)) && item->text()==def) item->setSelected(true);

    btn = new QPushButton("...");
    btn->setMaximumWidth(30);
    connect(btn, &QPushButton::clicked, dlgDecodeMode, &QDialog::exec);
    hhhh->addWidget(btn);
    grid->addLayout(hhhh, 1, 5);

    lb = new QLabel(tr("分组方式: "));
    grid->addWidget(lb, 2, 4);

    fdGroupMode = new QComboBox;
    fdGroupMode->addItem(tr("三线并行"));
    fdGroupMode->addItem(tr("三色1点串"));
    fdGroupMode->addItem(tr("三色8点串"));
    fdGroupMode->addItem(tr("三色16点串"));
    grid->addWidget(fdGroupMode, 2, 5);

    vv->addStretch();


    vv = new VBox(stack);
    vv->setContentsMargins(50, 50, 6, 6);

    lb = new QLabel(tr("数据极性选择"));
    gFont(lb, 16);
    lb->setPalette(bkBlue);
    vv->addWidget(lb);
    vv->addSpacing(50);

    auto hh = new HBox(vv);
    hh->addStretch();
    auto vvv = new VBox(hh);

    lb = new QLabel(tr("点击状态 1,状态 2,观察 LED 模块,选择全亮状态: "));
    vvv->addWidget(lb);
    vvv->addSpacing(30);

    hhhh = new HBox(vvv);
    hhhh->addStretch();

    auto fdPolaStat1 = new QRadioButton(tr("状态 1"));
    fdPolaStat1->setChecked(true);
    hhhh->addWidget(fdPolaStat1);
    hhhh->addStretch();

    auto fdPolaStat2 = new QRadioButton(tr("状态 2"));
    hhhh->addWidget(fdPolaStat2);
    hhhh->addStretch();

    auto fdDataPolarity = new QButtonGroup(fdPolaStat1);
    fdDataPolarity->addButton(fdPolaStat1, 1);
    fdDataPolarity->addButton(fdPolaStat2, 0);
    connect(fdDataPolarity, &QButtonGroup::idClicked, this, [this] (int id) {
        send(0x11, {0x01000000u | id});
    });

    vvv->addStretch();
    hh->addStretch();


    vv = new VBox(stack);
    vv->setContentsMargins(50, 50, 6, 6);

    lb = new QLabel(tr("OE极性选择"));
    gFont(lb, 16);
    lb->setPalette(bkBlue);
    vv->addWidget(lb);
    vv->addSpacing(50);

    hh = new HBox(vv);
    hh->addStretch();
    vvv = new VBox(hh);

    lb = new QLabel(tr("点击状态 1,状态 2,观察 LED 模块,选择高亮状态: "));
    vvv->addWidget(lb);
    vvv->addSpacing(30);

    hhhh = new HBox(vvv);
    hhhh->addStretch();

    auto fdOEStat1 = new QRadioButton(tr("状态 1"));
    fdOEStat1->setChecked(true);
    hhhh->addWidget(fdOEStat1);
    hhhh->addStretch();

    auto fdOEStat2 = new QRadioButton(tr("状态 2"));
    hhhh->addWidget(fdOEStat2);
    hhhh->addStretch();

    auto fdOePolarity = new QButtonGroup(fdOEStat1);
    fdOePolarity->addButton(fdOEStat1, 0);
    fdOePolarity->addButton(fdOEStat2, 1);
    connect(fdOePolarity, &QButtonGroup::idClicked, this, [this] (int id) {
        send(0x11, {0x01100000u | id});
    });

    vvv->addStretch();
    hh->addStretch();


    vv = new VBox(stack);
    vv->setContentsMargins(50, 50, 6, 6);

    lb = new QLabel(tr("扫描行数"));
    gFont(lb, 16);
    lb->setPalette(bkBlue);
    vv->addWidget(lb);
    vv->addSpacing(50);

    hh = new HBox(vv);
    hh->addStretch();

    vvv = new VBox(hh);

    lb = new QLabel(tr("根据亮线的行/列数确定扫描行/列数: "));
    vvv->addWidget(lb);
    vvv->addSpacing(30);

    hhhh = new HBox(vvv);
    hhhh->addStretch();

    fdRow = new QRadioButton(tr("行"));
    fdRow->setChecked(true);
    hhhh->addWidget(fdRow);
    hhhh->addStretch();

    fdCol = new QRadioButton(tr("列"));
    hhhh->addWidget(fdCol);
    hhhh->addStretch();

    auto grp = new QButtonGroup(fdRow);
    grp->addButton(fdRow, 0);
    grp->addButton(fdCol, 1);

    vvv->addSpacing(20);

    hhhh = new HBox(vvv);
    hhhh->addStretch();

    lb = new QLabel(tr("亮线的行/列数: "));
    hhhh->addWidget(lb);

    auto fdLightNum = new QSpinBox;
    fdLightNum->setRange(0, 9999);
    fdLightNum->setValue(1);
    hhhh->addWidget(fdLightNum);

    hhhh->addStretch();

    vvv->addStretch();

    hh->addStretch();

    vvv = new VBox(hh);

    lb = new QLabel(tr("芯片245版本: "));
    vvv->addWidget(lb);
    vvv->addSpacing(30);

    hhhh = new HBox(vvv);
    hhhh->addStretch();

    auto fdA = new QRadioButton("A");
    hhhh->addWidget(fdA);
    hhhh->addStretch();

    auto fdD = new QRadioButton("D");
    hhhh->addWidget(fdD);
    hhhh->addStretch();

    vvv->addStretch();
    hh->addStretch();


    vv = new VBox(stack);
    vv->setContentsMargins(50, 50, 6, 6);

    lb = new QLabel(tr("数据线颜色"));
    gFont(lb, 16);
    lb->setPalette(bkBlue);
    vv->addWidget(lb);
    vv->addSpacing(50);

    lb = new QLabel(tr("依次点击以下状态, 根据模组颜色选择对应颜色"));
    vv->addWidget(lb, 0, Qt::AlignCenter);
    vv->addSpacing(30);

    grid = new Grid(vv);
    grid->setVerticalSpacing(20);
    grid->setColumnStretch(0, 1);
    grid->setColumnStretch(2, 1);
    grid->setColumnStretch(7, 1);

    auto fdRGBStat = new QButtonGroup(grid);
    QButtonGroup *fdRGBs[3];
    for(int rr = 0; rr < 3; ++rr) {
        fdRGBStat->addButton(new QRadioButton(tr("状态")+QString::number(rr+1)), rr);
        fdRGBs[rr] = new QButtonGroup(grid);
        fdRGBs[rr]->addButton(new QRadioButton(tr("红")), 1);
        fdRGBs[rr]->addButton(new QRadioButton(tr("绿")), 2);
        fdRGBs[rr]->addButton(new QRadioButton(tr("蓝")), 3);
        fdRGBs[rr]->addButton(new QRadioButton(tr("黑")), 0);
        fdRGBs[rr]->button(rr+1)->setChecked(true);
        grid->addWidget(fdRGBStat->button(rr), rr, 1);
        grid->addWidget(fdRGBs[rr]->button(1), rr, 3);
        grid->addWidget(fdRGBs[rr]->button(2), rr, 4);
        grid->addWidget(fdRGBs[rr]->button(3), rr, 5);
        grid->addWidget(fdRGBs[rr]->button(0), rr, 6);
    }
    fdRGBStat->button(0)->setChecked(true);

    connect(fdRGBStat, &QButtonGroup::idClicked, this, [this] (int id) {
        send(0x11, {0x01200000u | id});
    });

    vv->addStretch();
    {
        auto vBox = new VBox(stack);
        vBox->setContentsMargins(0,0,0,0);

        auto hhh = new QHBoxLayout;

        auto btnUndo = new QPushButton("回撤");
        btnUndo->setMinimumSize(90, 30);
        connect(btnUndo, &QPushButton::clicked, this, [this] {
            auto lastIdx = tableRow->columnCount()-1;
            if(lastIdx==-1) return;
            auto last = tableRow->item(1, lastIdx);
            if(last->text()=="×") virtualCnt--;
            else {
                auto point = last->data(Qt::UserRole).toPoint();
                table->setText(point.y(), point.x(), "");
                if(lastIdx==virtualCnt) table->selectionModel()->clearCurrentIndex();
                else {
                    for(int i=lastIdx-1; i>-1; i--) {
                        if(tableRow->text(1, i)=="×") continue;
                        auto point = tableRow->data(1, i).toPoint();
                        table->setCurrentCell(point.y(), point.x());
                        break;
                    }
                }
            }
            tableRow->setColumnCount(lastIdx);
        });
        hhh->addWidget(btnUndo);

        auto btnAddVoid = new QPushButton(tr("插入虚点"));
        btnAddVoid->setMinimumSize(90, 30);
        connect(btnAddVoid, &QPushButton::clicked, this, [this] {
            virtualCnt++;
            auto cnt = tableRow->columnCount();
            tableRow->setColumnCount(cnt+1);
            tableRow->setText(0, cnt, QString::number(cnt+1))->setTextAlignment(Qt::AlignCenter);
            tableRow->setText(1, cnt, "×")->setTextAlignment(Qt::AlignCenter);
        });
        hhh->addWidget(btnAddVoid);

        auto btnReset = new QPushButton(tr("重新走点"));
        btnReset->setMinimumSize(90, 30);
        connect(btnReset, &QPushButton::clicked, this, [this] {
            table->clearContents();
            tableRow->setColumnCount(0);
            virtualCnt = 0;
            rows.clear();
        });
        hhh->addWidget(btnReset);

        auto btnOK = new QPushButton(tr("完成"));
        btnOK->setMinimumSize(90, 30);
        connect(btnOK, &QPushButton::clicked, this, &ExpertSmartPointSetWin::save);
        hhh->addWidget(btnOK);

        hhh->addStretch();
        vBox->addLayout(hhh);

        table = new Table;
        table->setEditTriggers(Table::NoEditTriggers);
        table->setSelectionMode(QTableWidget::SingleSelection);
        table->setColWidth(32)->setRowHeight(24)->setRowResize(QHeaderView::Fixed);
        connect(table, &QTableWidget::currentCellChanged, this, [=](int row, int col) {
            if(row < 0) return;
            auto text = table->text(row, col);
            if(! text.isEmpty()) return;
            if(rows.empty()) {
                auto cnt = tableRow->columnCount() + 1;
                tableRow->setColumnCount(cnt);
                tableRow->setText(0, cnt-1, QString::number(cnt))->setTextAlignment(Qt::AlignCenter);
                tableRow->setText(1, cnt-1, "√", QPoint{col, row})->setTextAlignment(Qt::AlignCenter);
                auto realCnt = cnt - virtualCnt;
                table->setText(row, col, QString::number(realCnt))->setTextAlignment(Qt::AlignCenter);
                if(realCnt < ModuleWidth*RowPerScan) {
                    if(send(0x11, {0x01400000u | cnt}) == 0) qDebug()<<"resp back. col:"<<QString::number(cnt, 16);
                } else {
                    QMessageBox::information(this, tr("提示"), tr("列走完"));
                    rows.emplace_back(tableRow->data(1, 1).toPoint());
                    send(0x11, {0x01400000u | (1<<10)});
                }
            } else if((int)rows.size() < ScanNum) {
                rows.emplace_back(QPoint{col, row});
                auto size = rows.size();
                table->setText(row, col, "S"+QString::number(size))->setTextAlignment(Qt::AlignCenter);
                if(size!=ScanNum) {
                    if(send(0x11, {0x01400000u | (size<<10)}) == 0) qDebug()<<"resp back. row:"<<QString::number(size, 16);
                } else {
                    QMessageBox::information(this, tr("提示"), tr("行走完"));
                    send(0x11, {0});
                    save();
                }
            }
        });

        vBox->addWidget(table);

        tableRow = new Table(2, 0);
        tableRow->setEditTriggers(Table::NoEditTriggers);
        tableRow->setSelectionMode(QTableWidget::NoSelection);
        tableRow->setColWidth(32)->setRowHeight(24)->setRowResize(QHeaderView::Fixed);
        tableRow->horizontalHeader()->setVisible(false);
        tableRow->verticalHeader()->setMinimumWidth(120);
        tableRow->setMaximumHeight(50);
        tableRow->setVHeaderText(0, tr("第一扫灯数:"));
        tableRow->setVHeaderText(1, tr("实: 虚:"));
        vBox->addWidget(tableRow);
    }

    auto hBox = new HBox(vBox);
    hBox->addStretch();

    auto btnPrev = new QPushButton(tr("上一步"));
    btnPrev->setMinimumSize(90, 30);
    hBox->addWidget(btnPrev);

    auto btnNext = new QPushButton(tr("下一步"));
    btnNext->setMinimumSize(90, 30);
    hBox->addWidget(btnNext);

    connect(btnPrev, &QPushButton::clicked, this, [stack, btnPrev, btnNext] {
        auto idx = stack->currentIndex();
        stack->setCurrentIndex(--idx);
        btnPrev->setEnabled(idx > 0);
        btnNext->setEnabled(idx < stack->count()-1);
    });
    connect(btnNext, &QPushButton::clicked, this, [=] {
        auto idx = stack->currentIndex();
        if(idx==0) {
            ModuleWidth = fdModuleWidth->value();
            ModuleHeight = fdModuleHeight->value();
            GroupNum = fdGroupNum->value();
            ScanNum = (ModuleHeight + GroupNum - 1) / GroupNum;
            ChipType = fdChipType->text();
            DecodeMode = fdDecodeMode->text();
            GroupMode = fdGroupMode->currentText();
            //                                                                                                       开始标识 保留 长度 换行间/刻  OE宽 放电间                    芯片通道数 模组类型数
            auto msg = QByteArray::fromHex("5555 01AD 0062 FFFFFFFF 0000ABCD B1000000 0000 415F94A7  AD000000 005C  AA55AA55 0000 0036 1770 0000 0280 0000 32 10 00 00 0A 01 00 00 001000 01  "
                                           "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 00000000 00000008 00200020 00001400 FF00FF00 3C000000 01000000 1B020010 00000020 55AA55AA 00000000 39F424F6 351B0E53");
            //                                                         UUID type      len      w   h    ex1     chip/decode ex3    smartset smartset2 走点len endFlag
            auto unitptr = msg.data()+headMap.end+modMap.Unit;
            auto w = (quint16_be*)(unitptr+modUnitMap.w);
            *w = ModuleWidth;
            auto h = (quint16_be*)(unitptr+modUnitMap.h);
            *h = ModuleHeight;
            auto chipType = (quint16_be*)(unitptr+modUnitMap.chipType);
            *chipType = ChipTypeCode;
            auto decodeMode = (quint16_be*)(unitptr+modUnitMap.decodeMode);
            *decodeMode = DecodeModeCode;

            auto smartset = unitptr+modUnitMap.smartset+3;
            *smartset = fdDataPolarity->checkedId();

            auto groupNum = unitptr+modUnitMap.smartsetRes + 1;
            *groupNum = GroupNum;
            auto scanNum = unitptr+modUnitMap.smartsetRes + 3;
            *scanNum = ScanNum;
            auto 走点len = (quint32_be*)(unitptr+modUnitMap.走点len);
            *走点len = (ModuleWidth+15) / 16 * 16;
            auto check = (quint32_be*)(msg.data()+headMap.end+modMap.check);
            *check = crc32_calc((byte*)msg.data()+headMap.end+modMap.换行时间, modMap.check - modMap.换行时间);
            check = (quint32_be*)(msg.data()+headMap.end+modMap.end);
            *check = crc32_calc((byte*)msg.data()+headMap.body, modMap.end+6);
            res12 = *(quint32_be*)(unitptr+modUnitMap.smartsetRes);
            if(expertWin->m_iRcvCardType!=enum_zrf)//added by alahover 20230904
            {
                auto waitingDlg = new WaitingDlg(this, tr("Setting")+" ...");
                auto res = sendMsgSync(msg, 0x1EA, 10000, waitingDlg);
                if(res.code==5) {QMessageBox::critical(this, "Error", tr("请求超时")); return;}
                else if(res.code) {QMessageBox::critical(this, "Error", QString(tr("发送失败: "))+QString::fromLocal8Bit(pcap_geterr(pcapSend))); return;}
                waitingDlg->close();
            }
        } else if(idx==1) {
            DataPolarity = fdDataPolarity->checkedId();
            res12 &= ~(1u<<31);
            res12 |= (uint)DataPolarity<<31;
            if(send(0x11, {0x01100000u | fdOePolarity->checkedId(), res12})) return;
        } else if(idx==2) {
            OePolarity = fdOePolarity->checkedId();
            res12 &= ~(1u<<30);
            res12 |= (uint)OePolarity<<30;
            if(send(0x11, {0x01300000u, res12})) return;
        } else if(idx==3) {
            RowPerScan = fdLightNum->value();
            ScanNum = (ScanNum + RowPerScan - 1) / RowPerScan;
            res12 &= ~0xffffu;
            res12 |= (uint)(RowPerScan - 1)<<8;  //折返次数
            res12 |= (uint)ScanNum;  //扫描类型
            if(send(0x11, {0x01200000u | fdRGBStat->checkedId(), res12, ((uint)ModuleWidth+15) / 16 * 16 * RowPerScan})) return;
        } else if(idx==4) {
            bool hases[4]{true,true,true,true};
            uint ids[4];
            for(int i=0; i<3; ++i) {
                ids[i] = fdRGBs[i]->checkedId();
                hases[ids[i]] = false;
            }
            for(int i=0; i<4; ++i) if(hases[i]) {
                ids[3] = i;
                break;
            }
            res12 &= ~(0xffu<<22);
            ColorMap = ids[0]<<6 | ids[1]<<4 | ids[2]<<2 | ids[3];
            res12 |= (uint)ColorMap << 22;
            if(send(0x11, {0x01400000u, res12})) return;
            table->setColumnCount(ModuleWidth);
            table->setRowCount(ModuleHeight);
            QColor altColor(0x445566);
            for(int r=0; r<ModuleHeight; r++) {
                if((r&7)!=7) for(int c=7; c<ModuleWidth; c+=8) table->itemValid(r, c)->setBackground(altColor);
                else for(int c=0; c<ModuleWidth; c++) table->itemValid(r, c)->setBackground(altColor);
            }
        }
        stack->setCurrentIndex(++idx);
        btnPrev->setEnabled(idx > 0);
        btnNext->setEnabled(idx < stack->count()-1);
    });

    auto btnCcl = new QPushButton("取消");
    connect(btnCcl, &QPushButton::clicked, this, [=] {
        send(0x11, {0});
        close();
    });
    btnCcl->setMinimumSize(90, 30);
    hBox->addWidget(btnCcl);

    hBox->addSpacing(30);
}

int ExpertSmartPointSetWin::send(uint ptr, const std::initializer_list<uint> &data) {
    auto msg = QByteArray::fromHex("5555 01AD 0000 FFFFFFFF 0000ABCD B1000000 0000 00000000  AD000000 0000");
    msg.append((data.size()+1)<<2, 0);
    auto plen = (quint16_be*)(msg.data()+headMap.len);
    *plen |= (quint16) (data.size()<<2)+6;
    plen = (quint16_be*)(msg.data()+headMap.bodylen);
    *plen |= (quint16) data.size()<<2;
    auto pptr = (quint32_be*)(msg.data()+headMap.ptr);
    *pptr |= ptr;
    pptr = (quint32_be*)(msg.data()+headMap.body);
    *pptr |= ptr;
    auto pdata = (quint32_be*) (msg.data()+headMap.end);
    for(auto dat : data) {
        *pdata = dat;
        pdata++;
    }
    auto check = (quint32_be*)(msg.data()+headMap.chk);
    *check = crc32_calc((byte*)msg.data()+2, headMap.chk-2);
    check = (quint32_be*)(msg.data()+msg.size()-4);
    *check = crc32_calc((byte*)msg.data()+headMap.body, msg.size()-4-headMap.body);

    if(expertWin->m_iRcvCardType!=enum_zrf)//added by alahover 20230904
    {
        auto waitingDlg = new WaitingDlg(this, tr("Setting")+" ...");
        auto res = sendMsgSync(msg, 0x1EA, 10000, waitingDlg);
        if(res.code==5) QMessageBox::critical(this, "Error", tr("请求超时"));
        else if(res.code) QMessageBox::critical(this, "Error", QString(tr("发送失败: "))+QString::fromLocal8Bit(pcap_geterr(pcapSend)));
        else waitingDlg->close();
        return res.code;
    }
    else
        return 0;
}

void ExpertSmartPointSetWin::save() {
    auto file = QApplication::applicationDirPath()+"/temp.module";
    QFile qFile(file);
    if(! qFile.open(QFile::WriteOnly)) {
        QMessageBox::critical(this, tr("失败"), QString(tr("准备写入 %1 文件失败")).arg(file));
        return;
    }
    JObj obj;
    obj.insert("ModuleWidth", ModuleWidth);
    obj.insert("ModuleHeight", ModuleHeight);
    obj.insert("GroupNum", GroupNum);
    obj.insert("ChipType", ChipType);
    obj.insert("DecodeMode", DecodeMode);
    obj.insert("GroupMode", GroupMode);
    obj.insert("ReverseGroup", ReverseGroup);
    obj.insert("DataPolarity", DataPolarity);
    obj.insert("OePolarity", OePolarity);
    obj.insert("ScanNum", ScanNum);
    obj.insert("ColorMap", ColorMap);
    obj.insert("RowPerScan", RowPerScan);
    JArray RowMap;
    for(QPoint row : rows) RowMap.append(row.y());
    obj.insert("RowMap", RowMap);
    JArray ColumnMap;
    auto cnt = tableRow->columnCount();
    if(cnt > ModuleWidth*RowPerScan) {
        auto add = cnt % ModuleWidth;
        if(add) add = ModuleWidth - add;
        for(int i=0; i<add; i++) ColumnMap.append(-1);
    }
    int *cols = new int[ModuleWidth]{0};
    for(int i=cnt-1; i>=0; i--) {
        auto data = tableRow->data(1, i);
        int col = -1;
        if(data.isValid()) {
            col = data.toPoint().x();
            cols[col]++;
            col += (RowPerScan-cols[col])*ModuleWidth;
        }
        ColumnMap.append(col);
    }
    delete [] cols;
    JArray pos_item_info;
    for(int i=cnt-1; i>=0; i--) {
        auto data = tableRow->data(1, i);
        auto pos = data.isValid() ? data.toPoint() : QPoint{-1, -1};
        pos_item_info.append(JObj{
            {"col", pos_item_info.size()},
            {"x", pos.x()},
            {"y", pos.y()}
        });
    }
    obj.insert("ColumnMap", ColumnMap);
    obj.insert("pos_item_info", pos_item_info);
    auto data = JToBytes(obj, "  ");
    auto res = qFile.write(data);
    qFile.close();
    if(res < 0) {
        QMessageBox::critical(this, tr("失败"), QString(tr("写入 %1 文件失败")).arg(file));
        return;
    }
    expertWin->mModule = obj;

    expertWin->fdModuleWidth->setText(QString::number(ModuleWidth));
    expertWin->fdModuleHeight->setText(QString::number(ModuleHeight));
    expertWin->fdGroupNum->setText(QString::number(GroupNum));
    expertWin->fdScanNum->setText(QString::number(ScanNum));
    expertWin->fdChipType->setText(ChipType);
    expertWin->m_effectStack->setCurrentIndex(GetIcTypeFromName(ChipType));
    expertWin->fdDecodeMode->setText(DecodeMode);

    QString strLine1,strLine2,strLine3,strLine4;
    int iLine_1=(ColorMap & 0b11000000) >> 6;
    int iLine_2=(ColorMap & 0b00110000) >> 4;
    int iLine_3=(ColorMap & 0b00001100) >> 2;
    int iLine_4=(ColorMap & 0b00000011) >> 0;
    switch(iLine_1)    {
        case 0b00:           strLine1="黑";        break;        case 0b01:            strLine1="红";        break;        case 0b10:            strLine1="绿";        break;        case 0b11:            strLine1="蓝";        break; }
    switch(iLine_2)    {
        case 0b00:           strLine2="黑";        break;        case 0b01:            strLine2="红";        break;        case 0b10:            strLine2="绿";        break;        case 0b11:            strLine2="蓝";        break; }
    switch(iLine_3)    {
        case 0b00:           strLine3="黑";        break;        case 0b01:            strLine3="红";        break;        case 0b10:            strLine3="绿";        break;        case 0b11:            strLine3="蓝";        break; }
    switch(iLine_4)    {
        case 0b00:           strLine4="黑";        break;        case 0b01:            strLine4="红";        break;        case 0b10:            strLine4="绿";        break;        case 0b11:            strLine4="蓝";        break; }

    expertWin->fdDataGroupColor->setText(strLine1+strLine2+strLine3+strLine4);
    expertWin->fdChouDian->setText("");

    expertWin->fdCardWidth->setValue(ModuleWidth);
    expertWin->fdCardHeight->setValue(ModuleHeight);
    //added by alahover -s 20230830
    expertWin->changeValueBySmart();
    //added by alahover -o 20230830

    auto appDir = QApplication::applicationDirPath();
    QDir(appDir).mkdir("ModuleFiles");
    file = QFileDialog::getSaveFileName(this, tr("保存文件"), appDir+QString("/ModuleFiles/%1X%2_%3扫.module").arg(ModuleWidth).arg(ModuleHeight).arg(ScanNum), tr("Module file (*.module)"));
    if(file.isEmpty()) return;
    QFile qFile2(file);
    if(! qFile2.open(QFile::WriteOnly)) {
        QMessageBox::critical(this, tr("失败"), QString(tr("准备写入 %1 文件失败")).arg(file));
        return;
    }
    res = qFile2.write(data);
    qFile2.close();
    if(res < 0) {
        QMessageBox::critical(this, tr("失败"), QString(tr("写入 %1 文件失败")).arg(file));
        return;
    }
    QMessageBox::information(this, tr("保存成功"), tr("保存成功"));
    close();
}