Ortec974A EPICS IOC程序

news2024/11/19 1:25:29

1) 创建一个用户存放这个IOC程序结构的目录:

root@orangepi4-lts:/usr/local/EPICS/program# mkdir ortec974A
root@orangepi4-lts:/usr/local/EPICS/program# cd ortec974A/
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A# ls

2)使用makeBaseApp.pl构建这个IOC程序架构:

root@orangepi4-lts:/usr/local/EPICS/program/ortec974A# makeBaseApp.pl -t ioc ortec974A
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A# makeBaseApp.pl -i -t ioc ortec974A
Using target architecture linux-aarch64 (only one available)
The following applications are available:
    ortec974A
What application should the IOC(s) boot?
The default uses the IOC's name, even if not listed above.
Application name?
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A# ls
configure  iocBoot  Makefile  ortec974AApp

3)编辑configure/RELEASE文件,添加这个IOC程序所需要的其它依赖模块:

...
SUPPORT=/usr/local/EPICS/synApps/support
ASYN=$(SUPPORT)/asyn
AUTOSAVE=$(SUPPORT)/autosave
CALC=$(SUPPORT)/calc
SCALER=$(SUPPORT)/scaler
...

4) 进入源文件目录ortec974AApp/src目录下:

a) 编写974A驱动程序源文件:

// drvOrtec974A.cpp
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <math.h>

#include <asynPortDriver.h>
#include <asynOctetSyncIO.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <epicsTime.h>
#include <errlog.h>
#include <iocsh.h>
#include <epicsExport.h> /* Defines epicsExportSharedSymbols */

#include "devScalerAsyn.h"

#define MAX_CHANNELS 4

#define timeOut 0.1
static const char *driverName= "Scaler974A";

class Scaler974A:public asynPortDriver
{
public:
    Scaler974A(const char *portName, const char *serialPort, int serialAddr, int poll);
    virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
    virtual asynStatus readInt32Array(asynUser *pasynUser,epicsInt32 *value, size_t nElements, size_t *nIn);
    virtual void report(FILE *fp, int details);
    void eventThread();

private:
    int scalerReset;
    int scalerChannels;
    int scalerRead;
    int scalerReadSingle;
    int scalerPreset;
    int scalerArm;
    int scalerDone;
    double polltime;
    epicsEventId eventId;
    asynUser *pasynUserScaler;
    asynStatus sendCommand(const char *command, char *statusString, size_t maxStatusLen, size_t *statusLen);
};

static void eventThreadC(void *pPvt)
{
    Scaler974A *pScaler974A = (Scaler974A *)pPvt;
    pScaler974A->eventThread();
}

Scaler974A::Scaler974A(const char *portName, const char *serialPort, int serialAddr, int poll)
    :asynPortDriver(portName, MAX_CHANNELS,
                    asynInt32Mask | asynInt32ArrayMask | asynDrvUserMask,
                    asynInt32Mask,
                    /* Should also be ASYN_CANBLOCK, but device support does not work with asynchronous devices */
                    ASYN_MULTIDEVICE,1,0,0)
{
    int i;
    asynStatus status;
    static const char *functionName="Scaler974A";

    if (poll==0) poll=100;
    this->polltime=poll / 1000.;
    this->eventId = epicsEventCreate(epicsEventEmpty);
    status = pasynOctetSyncIO->connect(serialPort, serialAddr, &this->pasynUserScaler, NULL);
    if (status != asynSuccess) {
        asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
            "%s:%s: error connecting to port %s address %d\n",
            driverName, functionName, serialPort, serialAddr);
        return;
    }

    createParam(SCALER_RESET_COMMAND_STRING,        asynParamInt32,     &this->scalerReset);
    createParam(SCALER_CHANNELS_COMMAND_STRING,     asynParamInt32,     &this->scalerChannels);
    createParam(SCALER_READ_COMMAND_STRING,         asynParamInt32Array,&this->scalerRead);
    createParam(SCALER_READ_SINGLE_COMMAND_STRING,  asynParamInt32,     &this->scalerReadSingle);
    createParam(SCALER_PRESET_COMMAND_STRING,       asynParamInt32,     &this->scalerPreset);
    createParam(SCALER_ARM_COMMAND_STRING,          asynParamInt32,     &this->scalerArm);
    createParam(SCALER_DONE_COMMAND_STRING,         asynParamInt32,     &this->scalerDone);

    setIntegerParam(scalerChannels, MAX_CHANNELS);
    setIntegerParam(scalerDone, 1);
    for (i=0; i<MAX_CHANNELS; i++) setIntegerParam(i, scalerReadSingle, 0);

    epicsThreadCreate("Scaler974A",
                      epicsThreadPriorityMedium,
                      epicsThreadGetStackSize(epicsThreadStackMedium),
                      (EPICSTHREADFUNC)eventThreadC,this);
}


asynStatus Scaler974A::sendCommand(const char *command, char *statusString, size_t maxStatusLen, size_t *statusLen)
{
    size_t nWrite;
    asynStatus status;
    double timeout = 1.0;
    int eomReason;
    static const char *functionName = "sendCommand";

/*
 * asynStatus (*writeRead)(asynUser *pasynUser,
                  const char *write_buffer, size_t write_buffer_len,
                  char *read_buffer, size_t read_buffer_len,
                  double timeout,
                  size_t *nbytesOut, size_t *nbytesIn, int *eomReason);
                  要写的字符串,要写的字符串长度,读出缓存,要读的字节数目,超时时间,实际写出的字节数,实际读入的字符数,结束原因

                  asynStatus (*write)(asynUser *pasynUser,  char const *buffer, size_t buffer_len,
                  double timeout,size_t *nbytesTransfered);

                    asynStatus (*read)(asynUser *pasynUser, char *buffer, size_t buffer_len,
                  double timeout, size_t *nbytesTransfered,int *eomReason);
 *
 * */
        status = pasynOctetSyncIO->write(this->pasynUserScaler, command, strlen(command),  timeout, &nWrite);
//      printf("In sendCommand write status: %d,write content:%s,write length:%d\n", status, command, nWrite);
        if (status != asynSuccess) goto done;

        epicsThreadSleep(.1);

        status = pasynOctetSyncIO->read(this->pasynUserScaler,statusString, maxStatusLen, timeout, statusLen, &eomReason);
//      printf("In sendCommand read status: %d, statusString:%s\n", status, statusString);

    done:
    if (status != asynSuccess) {
        asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
                  "%s:%s: writing command %s, error=%s\n",
                  driverName, functionName, command, this->pasynUserScaler->errorMessage);
    }
    return(status);
}

asynStatus Scaler974A::writeInt32(asynUser *pasynUser, epicsInt32 value)
{
    int function = pasynUser->reason;
    char response[256];
    size_t responseLen;
    asynStatus status = asynSuccess;

    static const char *functionName="writeInt32";
    setIntegerParam(function, value);

    if(function==this->scalerReset)
    {
        this->sendCommand("STOP\r", response, sizeof(response), &responseLen);
        //printf("STOP:%s\n", response);
        this->sendCommand("CLEAR_ALL\r", response, sizeof(response), &responseLen);
        //printf("CLEAR_ALL:%s\n", response);
        asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s:%s scalerReset\n",driverName, functionName);
    }
    else if(function==this->scalerArm)
    {
        if(value !=0)
        {
                /* Start counting */
            this->sendCommand("START\r", response, sizeof(response), &responseLen);
//          printf("START:%s\n", response);
            setIntegerParam(scalerDone, 0);
            epicsEventSignal(this->eventId);
        }
        else
        {
                /* Stop counting */
            status = this->sendCommand("STOP\r", response, sizeof(response), &responseLen);
//          printf("STOP:%s\n", response);
            setIntegerParam(scalerDone, 1);
        }
        asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s:%s scalerArm=%d\n", driverName, functionName,value);
    }
    else if(function==this->scalerPreset)
    {
        int m,n;
        char newstr[25];

        n=(int)log10(double(value));
        m=(int)(value/pow(10.0,n));

        sprintf(newstr, "SET_COUNT_PRESET %d,%d\r", m, n);
        this->sendCommand(newstr, response, sizeof(response), &responseLen);
//      printf("SET_COUNT_PRESET:%s\n", response);

        asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s;%s scalerPreset channel", driverName, functionName);
    }
    else
    {
        asynPrint(pasynUser,ASYN_TRACE_ERROR,"%s:%s got illegal function %d\n", driverName, functionName, function);
    }
    callParamCallbacks();
    return(status);
}

asynStatus Scaler974A::readInt32Array(asynUser *pasynUser, epicsInt32 *value, size_t maxChannel, size_t *nIn)
{
    static const char *functionName="readInt32Array";
    int function = pasynUser->reason;
    int temp;
    int i;

    if (maxChannel > MAX_CHANNELS) maxChannel = MAX_CHANNELS;
    *nIn = maxChannel;

    if (function==scalerRead)
    {
        for (i=0; i<(int)maxChannel; i++)
        {
            getIntegerParam(i, scalerReadSingle, &temp);
            value[i] = temp;
        }
        asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
            "%s:%s: value=%d %d %d %d\n",
            driverName, functionName, value[0], value[1], value[2], value[3]);
    } else {
        asynPrint(pasynUser,ASYN_TRACE_ERROR,"%s:%s got illegal function %d\n", driverName, functionName,  function);
        return(asynError);
    }
    return(asynSuccess);
}

void Scaler974A::report(FILE *fp, int details)
{
    asynPortDriver::report(fp, details);
}

void Scaler974A::eventThread()
{
    int done, presetCount;
    char response[100], statusString[256];
    size_t responseLen, statusLen;
    int counts[MAX_CHANNELS];
    int i;
    asynStatus status;
    static const char *functionName="eventThread";

    while(1)
    {
        epicsEventMustWait(this->eventId);
        while(1)
        {
            status = this->sendCommand("SHOW_COUNTS\r", statusString, sizeof(statusString), &statusLen);

            sscanf(statusString, "%d;%d;%d;%d;",
                   &counts[0], &counts[1], &counts[2], &counts[3]);
//          printf("%d;%d;%d;%d\n", counts[0],counts[1],counts[2],counts[3]);
            asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
                "%s:%s status=%d, counts=%d %d %d %d\n",
                driverName, functionName, status, counts[0], counts[1], counts[2], counts[3]);
            this->lock();
            /* Get value of done in case scaler was stopped by scalerArm(0) */
            getIntegerParam(scalerDone, &done);
            getIntegerParam(scalerPreset, &presetCount);
            if (!done && (counts[0] >= presetCount)) done = 1;
            setIntegerParam(scalerDone, done);
            for (i=0; i<MAX_CHANNELS; i++) {
                setIntegerParam(i, scalerReadSingle, counts[i]);
                callParamCallbacks(i, i);
            }
            this->unlock();
            if (done) break;
            epicsThreadSleep(this->polltime/1000.0);
        }
    }
}

extern "C" int initScaler974A(const char *portName, const char *serialPort, int serialAddr, int poll)
{
    new Scaler974A(portName, serialPort, serialAddr, poll);
    return(asynSuccess);
}

/* iocsh function */
static const iocshArg initArg0 = {"Port Name", iocshArgString};
static const iocshArg initArg1 = {"IPPort", iocshArgString};
static const iocshArg initArg2 = {"Addr", iocshArgString};
static const iocshArg initArg3 = {"Poll", iocshArgInt};
static const iocshArg *const initArgs[] = {&initArg0,
                                           &initArg1,
                                           &initArg2,
                                           &initArg3
                                          };
static const iocshFuncDef initFuncDef = {"initScaler974A",4, initArgs};
static void initCallFunc(const iocshArgBuf *args)
{
    initScaler974A(args[0].sval, args[1].sval, args[2].ival, args[3].ival);
}

void Scaler974ARegister(void)
{
    iocshRegister(&initFuncDef,initCallFunc);
}

extern "C" {
epicsExportRegistrar(Scaler974ARegister);
}

b) 添加一个dbd文件ortec974ASupport.dbd:

registrar(Scaler974ARegister)

c) 修改同一目录下Makefile文件,指定编译中需要包含的库文件和源文件:

TOP=../..

include $(TOP)/configure/CONFIG
#----------------------------------------
#  ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================

#=============================
# Build the IOC application

PROD_IOC = ortec974A
# ortec974A.dbd will be created and installed
DBD += ortec974A.dbd

# ortec974A.dbd will be made up from these files:
ortec974A_DBD += base.dbd
ortec974A_DBD += ortec974ASupport.dbd

# Include dbd files from all support applications:
#ortec974A_DBD += xxx.dbd

ortec974A_DBD += drvAsynIPPort.dbd
ortec974A_DBD += asyn.dbd
ortec974A_DBD += scalerSupport.dbd
ortec974A_DBD += calcSupport.dbd
ortec974A_DBD += asSupport.dbd


# Include dbd files from all support applications:
#ortec974A_DBD += xxx.dbd

# Add all the support libraries needed by this IOC
ortec974A_LIBS += asyn
ortec974A_LIBS += scaler
ortec974A_LIBS += calc
ortec974A_LIBS += autosave

# Add all the support libraries needed by this IOC
#ortec974A_LIBS += xxx

ortec974A_SRCS += drvOrtec974A.cpp
# ortec974A_registerRecordDeviceDriver.cpp derives from ortec974A.dbd
ortec974A_SRCS += ortec974A_registerRecordDeviceDriver.cpp

# Build the main IOC entry point on workstation OSs.
ortec974A_SRCS_DEFAULT += ortec974AMain.cpp
ortec974A_SRCS_vxWorks += -nil-

# Add support from base/src/vxWorks if needed
#ortec974A_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary

# Finally link to the EPICS Base libraries
ortec974A_LIBS += $(EPICS_BASE_IOC_LIBS)

#===========================

include $(TOP)/configure/RULES
#----------------------------------------
#  ADD RULES AFTER THIS LINE

5) 进入ortec974AApp/Db目录,编写一个用于自动保存的文件scaler4_settings.req:

$(P)$(S).TP
$(P)$(S).TP1
$(P)$(S).CONT
$(P)$(S).DLY1
$(P)$(S).RAT1
$(P)$(S).PREC
$(P)$(S).FREQ
$(P)$(S).RATE
$(P)$(S).DLY
$(P)$(S).COUT
file "scaler_channelN_settings.req", P=$(P), S=$(S), Ch=1
file "scaler_channelN_settings.req", P=$(P), S=$(S), Ch=2
file "scaler_channelN_settings.req", P=$(P), S=$(S), Ch=3
file "scaler_channelN_settings.req", P=$(P), S=$(S), Ch=4

在同一目录下的Makefile中添加一行:DB += scaler4_settings.req

6) 退出到这个IOC的顶层目录,执行make进行程序编译。

7)进入到启动目录iocBoot/iocortec974A,创建两个目录:

root@orangepi4-lts:/usr/local/EPICS/program/ortec974A/iocBoot/iocortec974A# mkdir -p req/ortec974A
root@orangepi4-lts:/usr/local/EPICS/program/ortec974A/iocBoot/iocortec974A# mkdir -p autosave/ortec974A

在req/ortec974A下编写一个用于自动保存的文件auto_settings.req,内容如下:

file scaler4_settings.req  P=$(P) S=$(S)

编写启动脚本:

#!../../bin/linux-aarch64/ortec974A

#- You may have to change ortec974A to something else
#- everywhere it appears in this file

< envPaths

cd "${TOP}"

## Register all support components
dbLoadDatabase "dbd/ortec974A.dbd"
ortec974A_registerRecordDeviceDriver pdbbase

drvAsynIPPortConfigure("ortec974A", "192.168.3.30:4001", 0, 0 ,1)

initScaler974A("tcportec974A","ortec974A",0,10)
dbLoadRecords("${SCALER}/db/scaler.db", "P=Ortec974A:,S=Scaler1,OUT=@asyn(tcportec974A 0 0),DTYP=Asyn Scaler,FREQ=10,TP=1,TP1=0.5,PR1=1,CONT=1")


set_requestfile_path("$(TOP)/db")
set_requestfile_path("$(SCALER)/db")
set_requestfile_path("$(TOP)/iocBoot/$(IOC)/req/ortec974A")

# 通过调用set_savefile_path函数指定你想要.sav文件被写到哪个目录中。
set_savefile_path("$(TOP)/iocBoot/$(IOC)/autosave/ortec974A")

# 使用set_pass<N>_restoreFile()函数
# 指定哪些save文件要在记录初始化前(pass 0)前被恢复,以及哪些save文件在记录初始化后(pass 1)被恢复
set_pass1_restoreFile("auto_settings.sav")

save_restoreSet_numSeqFiles(3)
save_restoreSet_SeqPeriodInSeconds(600)
save_restoreSet_RetrySeconds(60)
save_restoreSet_CAReconnect(1)
save_restoreSet_CallbackTimeout(-1)

dbLoadRecords("$(ASYN)/db/asynRecord.db","P=Ortec974A:Scaler1:,R=Asyn,PORT=ortec974A,ADDR=0,IMAX=100,OMAX=100")


cd "${TOP}/iocBoot/${IOC}"
iocInit

create_monitor_set("auto_settings.req",5,"P=Ortec974A:,S=Scaler1")

8) 用以上启动脚本启动这个IOC程序:

root@orangepi4-lts:/usr/local/EPICS/program/ortec974A/iocBoot/iocortec974A# ../../bin/linux-aarch64/ortec974A st.cmd

9)查看这个IOC加载的记录:

epics> dbl
Ortec974A:Scaler1:Asyn
Ortec974A:Scaler1

10) 用CSS编写操作客户端:

11) 在97A的3号输入端口接入一个3MHz的TTL信号,将计数时间设置为1.0秒,点击Count按钮,进行1秒钟计数:

通道3能够读出计数器的正确计数值。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1150185.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

系列四十二、Spring的事务传播行为案例演示(二)#REQUIRED

一、演示Spring的默认传播行为&#xff08;REQUIRED&#xff09; 1.1、运行之前表中的数据 1.2、StockServiceImpl /*** Author : 一叶浮萍归大海* Date: 2023/10/30 15:43* Description:*/ Service(value "stockServiceREQUIRED") public class StockServiceImpl…

Mysql设置了更新时间自动更新,指定更新部分sql时不进行时间更新

现象&#xff1a; 因为字段设置了自动更新&#xff0c;所以sql语句一进行修改此字段就会自动更新时间&#xff0c;但是呢我们的有部分定时任务是半夜执行&#xff0c;并且不能让这个任务修改到数据的更新时间 解决&#xff1a; <update id"updateCreative">ALT…

element-plus DateTimePicker日期选择器,限制指定日期和时间不可选择

element-plus日期选择器&#xff0c;在指定日期时间前不可选择。 限制日期选择&#xff0c;使用disabled-date 限制小时选择&#xff0c;使用disabled-hours 限制分钟选择&#xff0c;使用disabled-minutes 限制毫秒选择&#xff0c;使用disabled-seconds 指定日期当天的时间有…

一带一路10周年:爱创科技加速中国药企国际化征程

“源自中国&#xff0c;属于世界”。 共建“一带一路”倡议提出10周年来&#xff0c;中国与沿线国家经济深度融合&#xff0c;在共商共建共享的基本原则下&#xff0c;“一带一路”形成了国际合作的平台和机制&#xff0c;跨国经济合作已基本形成。 随着“一带一路”合作日益加…

【脚本笔记】AssetDatabase

AssetDatabase是编辑器下的处理资源操作的重要类&#xff0c;主要用于访问资源并针对资源执行操作的接口。 这里面所有的操作路径都是基于Unity项目的相对路径也就是Assets/xxx或者Assets/xxx.jpg这种。CacheServer 主要解决的是缩短大型团队导入资源的时间。当配置后&#xff…

K8S部署应用时从harbor拉取镜像失败

问题描述 K8S部署服务拉取镜像失败&#xff1a;ImagePullBackOff K8S拉取镜像提示&#xff1a;Failed to pull image “IP:PORT/zcy-project/nginx:1.16.1”: rpc error: code Unknown desc Error response from daemon: unauthorized: unauthorized to access repository: …

mediapipe 训练自有图像数据分类

参考&#xff1a; https://developers.google.com/mediapipe/solutions/customization/image_classifier https://colab.research.google.com/github/googlesamples/mediapipe/blob/main/examples/customization/image_classifier.ipynb#scrollToplvO-YmcQn5g 安装&#xff1a…

配音软件怎么选?推荐三款自用好评的

会刷短视频的小伙伴肯定知道&#xff0c;很多创作者想让自己的视频更加丰富&#xff0c;呈现更加完美的视频效果&#xff0c;往往会在视频里的空境部分加入一些旁白解说&#xff0c;你们难道就不好奇吗&#xff1f;这些声音为什么这么想真人说的话&#xff0c;而且还可以这么自…

虚幻C++基础 day1

虚幻C概念 虚幻C类的继承结构 虚幻引擎C类层级结构(Hierarchy) 这些基本类又派生出了很多子类&#xff0c;例&#xff1a; UE中的反射与垃圾回收系统 例如一个创建了一个Actor类&#xff0c;有一个Actor类型指针去指向这个Actor类&#xff0c;如果的指针被销毁了&#xff…

编译环境里存在yaml-cpp的多个版本时可能引起的问题

有时要编译的程序自带了特定版本的yaml-cpp&#xff0c;同时系统目录下也安装了更高版本的yaml-cpp&#xff0c;这时可能引起编译错误&#xff0c;就是某些yaml-cpp的API不认识&#xff0c;例如&#xff1a; 出现这种问题倒好办&#xff0c;正常情况下不可能&#xff0c;肯定能…

目标检测算法发展史

前言 比起图像识别&#xff0c;现在图片生成技术要更加具有吸引力&#xff0c;但是要步入AIGC技术领域&#xff0c;首先不推荐一上来就接触那些已经成熟闭源的包装好了再提供给你的接口网站&#xff0c;会使用别人的模型生成一些图片就能叫自己会AIGC了吗&#xff1f;那样真正…

电脑突然提示找不到msvcp140.dll怎么办,解决msvcp140.dll丢失的办法

当我们在电脑上运行某些软件或游戏时&#xff0c;可能会遇到一个常见的错误消息&#xff1a;“找不到msvcp140.dll”。出现这样的情况通常意味着系统缺少一个重要的动态链接库文件&#xff0c;而这可能会导致程序无法正常启动。如果你现在遇到了这个问题&#xff0c;哪有可以用…

人大与加拿大女王大学金融管理硕士项目:开启国际视野,成就金融领袖

生活中&#xff0c;我们总会遇到各种各样的困难和挑战。有时候&#xff0c;我们会感到沮丧、迷茫甚至绝望。但是&#xff0c;正是这些困难和挑战&#xff0c;让我们变得更加坚强、勇敢和成熟。在这个职场竞争愈发激烈的时代&#xff0c;不断地充实自己是非常重要的。如果你从事…

Echats-页面切换时echats图表刷新

在src文件夹下的composables创建echats.ts echats.ts的内容为&#xff1a; import { nextTick, effectScope, onScopeDispose, ref, watch } from vue; import type { ComputedRef, Ref } from vue; import * as echarts from echarts/core; import { BarChart, GaugeChart, L…

Golang | Zinx学习笔记(一)

参考 http://zinx.me/ https://www.kancloud.cn/aceld/zinx/1960213 https://www.yuque.com/aceld/tsgooa/gx01meg5ow4pftac 说明 zinx是一个基于Golang的轻量级并发服务器框架。 目前zinx已经在很多企业进行开发使用&#xff0c;具体使用领域包括:后端模块的消息中转、长链…

Mybatis-Plus通用枚举功能 [MyBatis-Plus系列] - 第493篇

历史文章&#xff08;文章累计490&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 S…

win 下安装 nvm 的使用与配置

nvm 全名 node.js version management&#xff0c;是一个 nodejs 的版本管理工具。通过它可以安装和切换不同版本的 nodejs。 注&#xff1a;如果已经安装了 nodejs 需先卸载后再安装 nvm 为了确保 nodejs 已彻底删除&#xff0c;可以看看安装目录中是否有 node 文件夹&#x…

Linux|安装Nomachine

参考&#xff1a;2022 Nomachine 最简安装与使用指南&#xff08;https://blog.csdn.net/qq_51116518/article/details/127450253&#xff09; 解压 先将目录调整到压缩包所在目录&#xff0c;输入sudo tar zxvf nomachine_7.6.2_3_aarch64.tar.gz 添加权限 sudo chmod -R…

基于Cmake+QT+VS的C++项目构建开发编译简明教程

目前项目是尽量利用开源项目为基础&#xff0c;考虑到跨平台&#xff0c;以及后期便于开发的协同和延续&#xff0c;开发语言基于C/C、UI基于QT&#xff08;5.7.1&#xff09;&#xff0c;集成开发环境&#xff08;IDE&#xff09;使用Visual Studio&#xff08;2015&#xff0…