Program run from supervisord cannot open X display

Operating system environment:

  1. Jetpack 6.0.
  2. Ubuntu 22.04.
  3. 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 supervisord. 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:

  1. Add environment variables, such as DISPLAY=:0.

  2. Output the value of the current environment variable DISPLAY as :0 inside Dispaly.cpp to confirm that the environment variable is correct.

  3. 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.

  4. 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.

[program:pollen]
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和用户自定义名字 | en:print current ip and user defined name
        printf("Device Model Name: %sn", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName);
        printf("CurrentIp: %d.%d.%d.%dn" , nIp1, nIp2, nIp3, nIp4);
        printf("UserDefinedName: %snn" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName);
    }
    else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE)
    {
        printf("Device Model Name: %sn", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName);
        printf("UserDefinedName: %snn", 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 environmentn");
        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[i];
                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相机有效) | en:Detection 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!nn");
        }
        else
        {
            printf("set AcquisitionFrameRate failed! nRet [%x]nn", 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!nn");
        }
        else
        {
            printf("set height failed! nRet [%x]nn", 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!nn");
        }
        else
        {
            printf("set width failed! nRet [%x]nn", nRet);
        }

        // 设置bool型变量
        // set IBoolean variable
        bool bAcquisitionFrameRateEnable = true;
        nRet = MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", bAcquisitionFrameRateEnable);
        if (MV_OK == nRet)
        {
            printf("Set AcquisitionFrameRateEnable OK!nn");
        }
        else
        {
            printf("Set AcquisitionFrameRateEnable Failed! nRet = [%x]nn", 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!nn");
        }
        else
        {
            printf("set ExposureAuto failed! nRet [%x]nn", 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!nn");
        }
        else
        {
            printf("set AutoExposureTimeUpperLimit failed! nRet [%x]nn", 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 = %dn",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;
}

4

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật