The invocation of C++ by Supervisor fails. Operating system environment: Jetpack 6.0. Ubuntu 22.04. aarch64. First, I have a C++ program for a Hikvision industrial camera. It calls the XOpenDisplay function of the X11 library to start a window and display the video stream of the camera. After compiling it, using ./Display or /bin/bash -c "/opt/MVS/Samples/aarch64/Display/Display" in the terminal can both start the window and see the video stream. Next, I encapsulated it using the following Python code to provide an API for access. After starting the FastAPI service through the following command /usr/bin/python /root/main.py in the terminal, accessing this API can also normally start the window and see the video stream. However, when I assign the task of starting the FastAPI web service to supervisor. After starting the web service normally, calling the API service prompts please run with screen environment. It can be found that an execution error occurs on the line of code dpy = XOpenDisplay(NIL);, which causes it to output the prompt. Finally, what should I do to enable the Python application managed by supervisor to call the C++ compiled program normally using subprocess? Attempts made: Add environment variables, such as DISPLAY=:0. Output the value of the current environment variable DISPLAY as :0 inside Dispaly.cpp to confirm that the environment variable is correct. Ensure that the code for starting the Python script is a shell script and use the supervisor configuration of command=/bin/bash -c "/usr/bin/python /path/to/my/python/main.py", but it still doesn't work. Use xhost + to allow all the user can connect to X server. The following is the FastAPI web code(main.py). from fastapi import FastAPI import subprocess as sp app = FastAPI() @app.get('/test_hik') def test_hik(): cmd = f'/bin/bash -c "/path/to/the/Display"' import os # sp.call(cmd, shell=True, cwd = config.DISPLAY_PATH.parent, env=dict(os.environ, DISPLAY=":0", XAUTHORITY="/home/jetson/.xsessionrc")) sp.call(cmd, shell=True, cwd = config.DISPLAY_PATH.parent) return {'status':True, 'msg':'Success start window grab images...'} if __name__ == '__main__': import uvicorn uvicorn.run(app) The following is the supervisor configuration. [programollen] autorestart=True autostart=True redirect_stderr=True command=/bin/bash -c "DISPLAY=:0 /bin/bash /path/to/my/python/main.sh" user=root directory=/root/pollen_new stdout_logfile_maxbytes=20MB stdout_logfile_backups=20 stdout_logfile=/var/log/pollen.log environment=DISPLAY=":0",MVCAM_SDK_PATH="/opt/MVS",MVCAM_COMMON_RUNENV="/opt/MVS/lib",MVCAM_GENICAM_CLPROTOCOL="/opt/MVS/lib/CLProtocol",ALLUSERSPROFILE="/opt/MVS/MVFG" The following is the Display C++ codes. #include <X11/Xlib.h> #include <assert.h> #include "math.h" #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> #include "MvCameraControl.h" #define NIL (0) bool g_bExit = false; Window g_hwnd; // 等待用户输入enter键来结束取流或结束程序 // wait for user to input enter to stop grabbing or end the sample program void PressEnterToExit(void) { int c; while ( (c = getchar()) != '\n' && c != EOF ); fprintf( stderr, "\nPress enter to exit.\n"); while( getchar() != '\n'); g_bExit = true; sleep(1); } bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo) { if (NULL == pstMVDevInfo) { printf("The Pointer of pstMVDevInfo is NULL!\n"); return false; } if (pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE) { int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24); int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16); int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8); int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff); // ch:打印当前相机ip和用户自定义名字 | enrint current ip and user defined name printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName); printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4); printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName); } else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE) { printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName); printf("UserDefinedName: %s\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName); } else { printf("Not support.\n"); } return true; } static void* WorkThread(void* pUser) { int nRet = MV_OK; MV_FRAME_OUT stImageInfo = {0}; MV_DISPLAY_FRAME_INFO stDisplayInfo = {0}; while(1) { nRet = MV_CC_GetImageBuffer(pUser, &stImageInfo, 1000); if (nRet == MV_OK) { //printf("Get Image Buffer: Width[%d], Height[%d], FrameNum[%d]\n", stImageInfo.stFrameInfo.nWidth, stImageInfo.stFrameInfo.nHeight, stImageInfo.stFrameInfo.nFrameNum); if (g_hwnd) { stDisplayInfo.hWnd = (void*)g_hwnd; stDisplayInfo.pData = stImageInfo.pBufAddr; stDisplayInfo.nDataLen = stImageInfo.stFrameInfo.nFrameLen; stDisplayInfo.nWidth = stImageInfo.stFrameInfo.nWidth; stDisplayInfo.nHeight = stImageInfo.stFrameInfo.nHeight; stDisplayInfo.enPixelType = stImageInfo.stFrameInfo.enPixelType; MV_CC_DisplayOneFrame(pUser, &stDisplayInfo); } nRet = MV_CC_FreeImageBuffer(pUser, &stImageInfo); if(nRet != MV_OK) { printf("Free Image Buffer fail! nRet [0x%x]\n", nRet); } } else { printf("Get Image fail! nRet [0x%x]\n", nRet); } if(g_bExit) { break; } } return 0; } int main() { Display *dpy; memset(&g_hwnd, 0, sizeof(Window)); dpy = NULL; // 打开连接到X服务器的连接 // open the connection to the display 0 dpy = XOpenDisplay(NIL); if (NULL == dpy) { printf("please run with screan environment\n"); return -1; } int whiteColor = WhitePixel(dpy, DefaultScreen(dpy)); g_hwnd = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, 752, 480, 0, 0xffff00ff, 0xff00ffff); // 获取改变窗口大小事件 // we want to get MapNotify events XSelectInput(dpy, g_hwnd, StructureNotifyMask |ExposureMask | KeyPressMask); // 使窗口可见 // "Map" the window (that is, make it appear on the screen) XMapWindow(dpy, g_hwnd); // 创建图像上下文给出绘图函数的属性 // Create a "Graphics Context" GC gc = XCreateGC(dpy, g_hwnd, 0, NIL); // 告诉GC使用白色 // Tell the GC we draw using the white color XSetForeground(dpy, gc, whiteColor); // 等待事件的到来 // Wait for the MapNotify event for(; { XEvent e; XNextEvent(dpy, &e); if (e.type == MapNotify) { break; } } int nRet = MV_OK; void* handle = NULL; do { MV_CC_DEVICE_INFO_LIST stDeviceList; memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); // 枚举设备 // enum device nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList); if (MV_OK != nRet) { printf("MV_CC_EnumDevices fail! nRet [%x]\n", nRet); break; } if (stDeviceList.nDeviceNum > 0) { for (int i = 0; i < stDeviceList.nDeviceNum; i++) { printf("[device %d]:\n", i); MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo; if (NULL == pDeviceInfo) { break; } PrintDeviceInfo(pDeviceInfo); } } else { printf("Find No Devices!\n"); break; } // printf("Please Intput camera index: "); unsigned int nIndex = 0; // scanf("%d", &nIndex); if (nIndex >= stDeviceList.nDeviceNum) { printf("Intput error!\n"); break; } // 选择设备并创建句柄 // select device and create handle nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]); if (MV_OK != nRet) { printf("MV_CC_CreateHandle fail! nRet [%x]\n", nRet); break; } // 打开设备 // open device nRet = MV_CC_OpenDevice(handle); if (MV_OK != nRet) { printf("MV_CC_OpenDevice fail! nRet [%x]\n", nRet); break; } // ch:探测网络最佳包大小(只对GigE相机有效) | enetection network optimal package size(It only works for the GigE camera) if (stDeviceList.pDeviceInfo[nIndex]->nTLayerType == MV_GIGE_DEVICE) { int nPacketSize = MV_CC_GetOptimalPacketSize(handle); if (nPacketSize > 0) { nRet = MV_CC_SetIntValue(handle,"GevSCPSPacketSize",nPacketSize); if(nRet != MV_OK) { printf("Warning: Set Packet Size fail nRet [0x%x]!\n", nRet); } } else { printf("Warning: Get Packet Size fail nRet [0x%x]!\n", nPacketSize); } } // 设置float型变量 // set IFloat variable float fAcquisitionFrameRate = 20.0f; nRet = MV_CC_SetFloatValue(handle, "AcquisitionFrameRate", fAcquisitionFrameRate); if (MV_OK == nRet) { printf("set AcquisitionFrameRate OK!\n\n"); } else { printf("set AcquisitionFrameRate failed! nRet [%x]\n\n", nRet); } // 设置int型变量 height // set IInteger variable unsigned int nHeightValue = 1080; // 宽高设置时需考虑步进(16),即设置宽高需16的倍数 // Step (16) should be considered when setting width and height, that is the width and height should be a multiple of 16 nRet = MV_CC_SetIntValue(handle, "Height", nHeightValue); if (MV_OK == nRet) { printf("set height OK!\n\n"); } else { printf("set height failed! nRet [%x]\n\n", nRet); } unsigned int nWidthValue = 1440; // 宽高设置时需考虑步进(16),即设置宽高需16的倍数 // Step (16) should be considered when setting width and height, that is the width and height should be a multiple of 16 nRet = MV_CC_SetIntValue(handle, "Width", nWidthValue); if (MV_OK == nRet) { printf("set width OK!\n\n"); } else { printf("set width failed! nRet [%x]\n\n", nRet); } // 设置bool型变量 // set IBoolean variable bool bAcquisitionFrameRateEnable = true; nRet = MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", bAcquisitionFrameRateEnable); if (MV_OK == nRet) { printf("Set AcquisitionFrameRateEnable OK!\n\n"); } else { printf("Set AcquisitionFrameRateEnable Failed! nRet = [%x]\n\n", nRet); } // 设置enum型变量 ExposureAuto // set IEnumeration variable unsigned int nExposureAuto = 2;//Continuous nRet = MV_CC_SetEnumValue(handle, "ExposureAuto", nExposureAuto); if (MV_OK == nRet) { printf("set ExposureAuto OK!\n\n"); } else { printf("set ExposureAuto failed! nRet [%x]\n\n", nRet); } // 设置int型变量 AutoExposureTimeUpperLimit // set IInteger variable unsigned int nAutoExposureTimeUpperLimit = 25000; nRet = MV_CC_SetIntValue(handle, "AutoExposureTimeUpperLimit", nAutoExposureTimeUpperLimit); if (MV_OK == nRet) { printf("set AutoExposureTimeUpperLimit OK!\n\n"); } else { printf("set AutoExposureTimeUpperLimit failed! nRet [%x]\n\n", nRet); } // 开始取流 // start grab image nRet = MV_CC_StartGrabbing(handle); if (MV_OK != nRet) { printf("MV_CC_StartGrabbing fail! nRet [%x]\n", nRet); break; } pthread_t nThreadID; nRet = pthread_create(&nThreadID, NULL ,WorkThread , handle); if (nRet != 0) { printf("thread create failed.ret = %d\n",nRet); break; } XEvent event; fprintf( stderr, "\nPress q to exit.\n"); while (1) { XNextEvent(dpy, &event); if (event.type == Expose) { // Draw or redraw the window here } else if (event.type == KeyPress) { KeySym keysym = XLookupKeysym(&event.xkey, 0); if (keysym == 'q') { printf("Quitting...\n"); g_bExit = true; break; } } sleep(1); } // PressEnterToExit(); // 停止取流 // stop grab image nRet = MV_CC_StopGrabbing(handle); if (MV_OK != nRet) { printf("MV_CC_StopGrabbing fail! nRet [%x]\n", nRet); break; } // 关闭设备 // close device nRet = MV_CC_CloseDevice(handle); if (MV_OK != nRet) { printf("MV_CC_CloseDevice fail! nRet [%x]\n", nRet); break; } // 销毁句柄 // destroy handle nRet = MV_CC_DestroyHandle(handle); if (MV_OK != nRet) { printf("MV_CC_DestroyHandle fail! nRet [%x]\n", nRet); break; } handle = NULL; }while (0); if (handle != NULL) { MV_CC_DestroyHandle(handle); handle = NULL; } printf("exit.\n"); return 0; } Continue reading...