Пример #1
0
    def openValkka(self):
        self.cpu_scheme = CPUScheme()

        # self.dm.camera_collection
        try:
            memory_config = next(
                self.dm.config_collection.get(
                    {"classname": DataModel.MemoryConfigRow.__name__}))
        except StopIteration:
            print(pre, "Using default mem config")
            memory_config = default.memory_config

        n_frames = round(
            memory_config["msbuftime"] * default.fps /
            1000.)  # accumulated frames per buffering time = n_frames

        if (memory_config["bind"]):
            self.cpu_scheme = CPUScheme()
        else:
            self.cpu_scheme = CPUScheme(n_cores=-1)

        self.gpu_handler = GPUHandler(
            n_720p=memory_config["n_720p"] * n_frames,  # n_cameras * n_frames
            n_1080p=memory_config["n_1080p"] * n_frames,
            n_1440p=memory_config["n_1440p"] * n_frames,
            n_4K=memory_config["n_4K"] * n_frames,
            msbuftime=memory_config["msbuftime"],
            verbose=False,
            cpu_scheme=self.cpu_scheme)

        self.livethread = LiveThread(name="live_thread",
                                     verbose=False,
                                     affinity=self.cpu_scheme.getLive())

        self.usbthread = USBDeviceThread(name="usb_thread",
                                         verbose=False,
                                         affinity=self.cpu_scheme.getUSB())

        self.filterchain_group = FilterChainGroup(datamodel=self.dm,
                                                  livethread=self.livethread,
                                                  usbthread=self.usbthread,
                                                  gpu_handler=self.gpu_handler,
                                                  cpu_scheme=self.cpu_scheme)
        self.filterchain_group.read()
        # self.filterchain_group.update() # TODO: use this once fixed

        try:
            from valkka.mvision import multiprocess
        except ImportError:
            pass
        else:
            if self.mvision:
                self.thread = multiprocess.QValkkaThread()
                self.thread.start()
Пример #2
0
    def setupUi(self):
        self.setGeometry(QtCore.QRect(100, 100, 800, 800))

        self.valkkafs = ValkkaFS.loadFromDirectory(
            dirname="/home/sampsa/tmp/testvalkkafs")
        self.manager = ValkkaFSManager(self.valkkafs)
        self.manager.setOutput_(925412, 1)  # id => slot

        gpu_handler = GPUHandler()

        pvc = PlayVideoContainerNxM(n_dim=3,
                                    m_dim=3,
                                    valkkafsmanager=self.manager,
                                    gpu_handler=gpu_handler,
                                    filterchain_group=None)

        # dummy window
        self.w = QtWidgets.QWidget(self)
        self.setCentralWidget(self.w)
        self.lay = QtWidgets.QVBoxLayout(self.w)
Пример #3
0
def test1():
    dm = DataModel()
    dm.clearAll()
    dm.saveAll()

    collection = dm.camera_collection

    collection.new(
        RTSPCameraRow, {
            "slot": 1,
            "address": "192.168.1.41",
            "username": "******",
            "password": "******",
            "tail": ""
        })

    collection.new(
        RTSPCameraRow, {
            "slot": 2,
            "address": "192.168.1.42",
            "username": "******",
            "password": "******",
            "tail": ""
        })

    for element in collection.get():
        print("test1 : 1", element)

    gpu_handler = GPUHandler(n_720p=5,
                             n_1080p=5,
                             n_1440p=0,
                             n_4K=0,
                             msbuftime=300,
                             verbose=False)

    livethread = LiveThread(
        name="live_thread",
        # verbose = True,
        verbose=False,
        # affinity = self.pardic["live affinity"]
    )

    filterchain_group = FilterChainGroup(datamodel=dm,
                                         livethread=livethread,
                                         gpu_handler=gpu_handler,
                                         verbose=True)
    filterchain_group.update()

    print("\n ADDING ONE \n")

    collection.new(
        RTSPCameraRow, {
            "slot": 3,
            "address": "192.168.1.43",
            "username": "******",
            "password": "******",
            "tail": ""
        })

    filterchain_group.update()

    print("\n REMOVING ONE \n")

    entry = next(collection.get({"address": "192.168.1.41"}))
    collection.delete(entry["_id"])

    filterchain_group.update()

    print("\n BYE \n")

    filterchain_group.close()
Пример #4
0
class MyGui(QtWidgets.QMainWindow):

    #config_dir = setValkkaLocalDir("live", varname = "config_dir")
    #valkkafs_dir = setValkkaLocalDir("live","fs", varname = "valkkafs_dir")

    def __init__(self, parent=None):
        """ctor
        """
        super(MyGui, self).__init__()
        self.initDirs()
        self.initVars()
        self.initConfigFiles()
        self.readDB()
        self.generateMethods()
        self.setupUi()
        self.startProcesses()
        self.openValkka()
        self.makeLogic()
        self.post()
        

    def getMargins(self):
        # https://doc.qt.io/qt-5/application-windows.html#x11-peculiarities
        if singleton.dx > 0:
            return
        singleton.dy = self.geometry().y() - self.y() # y() : with frame, geometry().y() : without frame
        singleton.dx = self.geometry().x() - self.x()
        singleton.dw = self.frameGeometry().width() - self.width()
        singleton.dh = self.frameGeometry().height() - self.height()
        print("getMargins: dy, dx, dw, dh", singleton.dy, singleton.dx, singleton.dw, singleton.dh)
        # dy, dx, dw, dh 29 4 8 33
        # WARNING!  Must move main window before this starts to give any values other than zero ..!
        
    # *** redefined Qt member functions ***
    
    def closeEvent(self, e):
        """Triggered when the main qt program exits
        """
        print("gui : closeEvent!")
        self.closeContainers()

        # self.manage_cameras_win.unSetPropagate() # don't send signals .. if you don't do this: close => closeEvent => will trigger self.reOpen
        # self.manage_cameras_win.close()
        
        self.camera_list_win.unSetPropagate()
        self.camera_list_win.close()
        
        self.config_win.unSetPropagate()
        self.config_win.close()

        self.closeValkka()
        singleton.data_model.close()
        
        self.closeProcesses()
        e.accept()
    

    def initDirs(self):
        self.config_dir = singleton.config_dir
        self.valkkafs_dir = singleton.valkkafs_dir


    def initVars(self):
        """Define files & variables
        """
        self.version_file = self.config_dir.getFile("version")
        self.layout_file = self.config_dir.getFile("layout")
        
        # singleton.thread = None # a QThread that reads multiprocessing pipes
        
        self.containers_grid = [] # list of instances of valkka.live.container.grid.VideoContainerNxM
        self.containers_playback = []
        
        self.mvision_classes, self.mvision_client_classes, self.mvision_master_classes =\
            tools.scanMVisionClasses(
                singleton.mvision_package_names
            ) 
    
        if (len(self.mvision_classes) > 0 or len(self.mvision_client_classes) > 0):
            self.mvision = True
        else:
            self.mvision = False

        self.valkkafs = None

        self.config_modified = False
        self.valkkafs_modified = False


    def initConfigFiles(self):
        self.first_start = True
        ver = self.readVersionNumber()
        if ver is not None:  # this indicates that the program has been started earlier
            print("valkka.live : loading config file for version number", ver)
            if ver:
                if (ver[0] == version.VERSION_MAJOR and ver[1] == version.VERSION_MINOR):
                    self.first_start = False
                else: # incorrect version number
                    print("valkka.live : clearing config")
                    pass
                    # .. or handle migration somehow
                    
        if self.first_start:  # first time program start
            # TODO: eula could be shown here
            print(pre, "initConfigFiles : first start")
            self.config_dir.reMake()
            self.saveVersionNumber()
            # self.saveConfigFile()
            # self.saveWindowLayout() # clears window layout
            self.first_start = True


    def readDB(self):
        """Datamodel includes the following files: config.dat, devices.dat
        """
        singleton.data_model = DataModel(directory = self.config_dir.get())
        # singleton.data_model = DataModel(directory = tools.getConfigDir())
        if (self.first_start):
            print(pre, "readDB : first start")
            singleton.data_model.clearAll()
            singleton.data_model.saveAll()

        # If camera collection is corrupt
        if not singleton.data_model.checkCameraCollection():
            singleton.data_model.clearCameraCollection()


    def saveVersionNumber(self):
        with open(self.version_file, "w") as f:
            f.write(version.get())
        

    def readVersionNumber(self):
        try:
            with open(self.version_file, "r") as f:
                st = f.read()
            vs = []
            for s in st.split("."):
                vs.append(int(s))
        except:
            print("valkka.live : could not read version number")
            return None
        else:
            return vs


    def saveWindowLayout(self):
        self.serializeContainers()
        

    def loadWindowLayout(self):
        self.closeContainers()
        self.deSerializeContainers()

    # *** Generate Qt structures ***

    def generateMethods(self):
        """Autogenerate some member functions
        
        - Generates slot functions for launching containers
        """
        for i in range(1, 5):
            # adds member function grid_ixi_slot(self)
            self.makeGridSlot(i, i)
            self.makePlaybackGridSlot(i, i)

        for cl in self.mvision_classes:
            self.makeMvisionSlot(cl)

        for cl in self.mvision_client_classes:
            self.makeMvisionClientSlot(cl)
    


    def setupUi(self):
        self.setStyleSheet(style.main_gui)
        self.setWindowTitle(singleton.program_name)

        self.setGeometry(QtCore.QRect(100, 100, 500, 500))

        self.w = QtWidgets.QWidget(self)
        self.setCentralWidget(self.w)

        self.filemenu = FileMenu(parent=self)
        self.viewmenu = ViewMenu(parent=self)  # grids up to 4x4
        self.configmenu = ConfigMenu(parent=self)

        if self.mvision:
            mvision_elements = []

            for cl in self.mvision_classes + self.mvision_client_classes:
                if cl.auto_menu:
                    el = QuickMenuElement(title = cl.name, method_name = cl.name)
                    mvision_elements.append(el)

            class MVisionMenu(QuickMenu):
                title = "Machine Vision"
                elements = mvision_elements

            self.mvisionmenu = MVisionMenu(parent = self)

        self.aboutmenu = AboutMenu(parent=self)

        # create container and their windows
        self.manage_cameras_container = singleton.data_model.getDeviceListAndForm(None)
        self.manage_memory_container = singleton.data_model.getConfigForm()
        self.manage_valkkafs_container = singleton.data_model.getValkkaFSForm()

        self.manage_memory_container.signals.save.connect(self.config_modified_slot)
        self.manage_cameras_container.getForm().signals.save_record.connect(self.config_modified_slot)
        self.manage_valkkafs_container.signals.save.connect(self.valkkafs_modified_slot)

        self.config_win = QTabCapsulate(
                "Configuration",
                [ 
                    (self.manage_cameras_container. widget, "Camera Configuration"),
                    (self.manage_memory_container.  widget, "Memory Configuration"),
                    (self.manage_valkkafs_container.widget, "Recording Configuration")
                ]
            )

        self.config_win.signals.close.connect(self.config_dialog_close_slot)
        # when the configuration dialog is reopened, inform the camera configuration form .. this way it can re-check if usb cams are available
        self.config_win.signals.show.connect(self.manage_cameras_container.getForm().show_slot) 
        self.config_win.signals.show.connect(self.manage_cameras_container.choose_first_slot) # so that we have at least one device chosen

        self.makeCameraTree()
        self.camera_list_win = QCapsulate(self.treelist, "Camera List")

        self.wait_label = QtWidgets.QLabel("Restarting Valkka, please wait ..")
        self.wait_window = QCapsulate(self.wait_label, "Wait", nude = True)


    def makeCameraTree(self):
        self.root = HeaderListItem()
        self.treelist = BasicView(parent = None, root = self.root)
        self.updateCameraTree()


    def updateCameraTree(self):
        self.treelist.reset_()

        self.server = ServerListItem(
            name = "Localhost", ip = "127.0.0.1", parent = self.root)
        """
        self.server1 = ServerListItem(
            name="First Server", ip="192.168.1.20", parent=self.root)
        """
        """
        self.camera1 = RTSPCameraListItem(camera=RTSPCameraDevice(
            ip="192.168.1.4", username="******", password="******"), parent=self.server1)
        self.camera2 = RTSPCameraListItem(camera=RTSPCameraDevice(
            ip="192.168.1.4", username="******", password="******"), parent=self.server1)
        """
        devices = []

        for row in singleton.data_model.camera_collection.get():
            # print(pre, "makeCameraTree : row", row)
            if (row["classname"] == RTSPCameraRow.__name__):
                row.pop("classname")
                devices.append(
                    RTSPCameraListItem(
                        camera = RTSPCameraDevice(**row),
                        parent = self.server
                    )
                )
            elif (row["classname"] == USBCameraRow.__name__):
                row.pop("classname")
                devices.append(
                    USBCameraListItem(
                        camera = USBCameraDevice(**row),
                        parent = self.server
                    )
                )
                
        self.treelist.update()
        self.treelist.expandAll()


    def makeLogic(self):
        # *** When camera list has been closed, re-create the cameralist tree and update filterchains ***
        # self.manage_cameras_win.signals.close.connect(self.updateCameraTree) # now put into save_camera_config_slot
        
        # self.manage_cameras_win.signals.close.connect(self.filterchain_group.update) # TODO: use this once fixed
        # self.manage_cameras_win.signals.close.connect(self.filterchain_group.read) # TODO: eh.. lets be sure of this .. (are we releasing slots in the LiveThread etc.)
        
        # self.manage_cameras_win.signals.close.connect(self.save_camera_config_slot)
        # self.manage_memory_container.signals.save.connect(self.save_memory_conf_slot)

        # *** Menu bar connections ***
        # the self.filemenu.exit attribute was autogenerated
        self.filemenu.exit.               triggered.connect(self.exit_slot)
        self.filemenu.save_window_layout. triggered.connect(self.save_window_layout_slot)
        self.filemenu.load_window_layout. triggered.connect(self.load_window_layout_slot)

        """
        self.configmenu.manage_cameras.   triggered.connect(
            self.manage_cameras_slot)
        self.configmenu.memory_usage.     triggered.connect(
            self.memory_usage_slot)
        """
        
        self.configmenu.configuration_dialog.triggered.connect(self.config_dialog_slot)
        
        self.viewmenu.camera_list.        triggered.connect(self.camera_list_slot)
        self.aboutmenu.about_program.     triggered.connect(self.about_slot)

        # *** Connect autogenerated menu calls into autogenerated slot functions ***
        for i in range(1, 5):
            # gets member function grid_ixi_slot
            slot_func = getattr(self, "grid_%ix%i_slot" % (i, i))
            # gets member function grid_ixi from self.viewmenu.video_grid
            menu_func = getattr(self.viewmenu.video_grid,
                                "grid_%ix%i" % (i, i))
            menu_func.triggered.connect(slot_func)
            # i.e., like this : self.viewmenu.video_grid.grid_1x1.triggered.connect(slot_func)

        for i in range(1, 5):
            # gets member function grid_ixi_slot
            slot_func = getattr(self, "playback_grid_%ix%i_slot" % (i, i))
            # gets member function grid_ixi from self.viewmenu.video_grid
            menu_func = getattr(self.viewmenu.playback_video_grid,
                                "grid_%ix%i" % (i, i))
            menu_func.triggered.connect(slot_func)
            # i.e., like this : self.viewmenu.video_grid.grid_1x1.triggered.connect(slot_func)


        # *** autogenerated machine vision menu and slots ***
        for cl in self.mvision_classes + self.mvision_client_classes:
            slot_func_name = cl.name+"_slot"
            if hasattr(self, slot_func_name):
                getattr(self.mvisionmenu,cl.name).triggered.connect(getattr(self,slot_func_name))


    def post(self):
        pass


    # *** Container handling ***

    def serializeContainers(self):
        """Serializes the current view of open video grids (i.e. the view)
        
        returns a dictionary where the keys are complete classnames
        each value corresponds to a list of containers of the class described by the key
        
        each serialized container looks like this:
        
        ::
        
            dic={
                "kwargs"     : {}, # parameters that we're used to instantiate this class
                }


        A concrete example:

        ::

            {'valkka.live.container.grid.VideoContainerNxM': [
                {   # individual serialized container
                    'child_class': <class 'valkka.live.container.video.VideoContainer'>,
                    'child_pars': [{'device_id': -1}],
                    'geom': (604, 0, 300, 300),
                    'm_dim': 1,
                    'n_dim': 1,
                    'n_xscreen': 0,
                    'title': 'Video Grid'
                },
                ...
                ]
            }

        - TODO: this stuff should be moved to the db .. ?  Or just keep using files..?
        - Different row types: 
            VideoContainerNxM : columns: child_class, child_pars, geom, etc.., LAYOUT_ID
            PlayVideoContainerNxM : .., LAYOUT_ID
            CameraListWindow : .., LAYOUT_ID
        - LAYOUT_ID identifies to which layout they belong

        """
        """
        container_list = [] # list of instances of classes in valkka.live.container, e.g. valkka.live.container.grid.VideoContainerNxM, etc.
        
        for container in self.containers_grid:
            # these are of the type valkka.live.container.grid.VideoContainerNxM
            print("gui: serialize containers : container=", pformat(container))
            container_list.append(container.serialize())
        
        # TODO: serialize self.containers_playback

        # classnames compatible with local namespace
        return {
            "valkka.live.container.grid.VideoContainerNxM"   : container_list
            }
        """
        singleton.data_model.layout_collection.clear()

        container_list = []
        for container in self.containers_grid:
            ser = container.serialize()
            # print(ser)
            # {'title': 'Video Grid', 'n_xscreen': 0, 'child_class': <class 'valkka.live.container.video.VideoContainer'>, 
            # 'child_pars': [{'device_id': -1}, {'device_id': -1}, {'device_id': -1}, {'device_id': -1}], 'geom': (604, 0, 300, 300), 'n_dim': 2, 'm_dim': 2}
            # singleton.data_model.layout_collection.new(VideoContainerNxMRow, ser) # nopes ..
            ser.update({"type":"VideoContainerNxM"})
            container_list.append(ser)

        for container in self.containers_playback:
            ser = container.serialize()
            ser.update({"type":"PlayVideoContainerNxM"})
            container_list.append(ser)

        ser = {"type": "QMainWindow", "geom": getCorrectedGeom(self)}
        container_list.append(ser)
        if self.camera_list_win.isVisible():
            ser = {"type": "CameraListWindow", "geom": getCorrectedGeom(self.camera_list_win)}
            container_list.append(ser)

        singleton.data_model.layout_collection.new(LayoutContainerRow, {"layout" : container_list})

        print(singleton.data_model.layout_collection)
        singleton.data_model.layout_collection.save()
        

    def deSerializeContainers(self):
        """Re-creates containers, based on the list saved into layout_collection
        
        This is the inverse of self.serializeContainers
        
        Containers must be closed & self.contiainers etc. list must be cleared before calling this
        """
        # glo = globals()
        # print("glo>",glo)
        singleton.reCacheDevicesById() # singleton.devices_by_id will be used by the containers
        
        try:
            row = next(singleton.data_model.layout_collection.get())
        except StopIteration:
            return
        
        container_list = row["layout"]

        # print(">", container_list)
        for container_dic in container_list:
            t = container_dic.pop("type") # get the type & remove it from the dict

            if t == "VideoContainerNxM":
                container_dic["child_class"] = nameToClass(container_dic.pop("child_class")) # swap from class name to class instance
                container_dic["geom"] = tuple(container_dic["geom"])  # woops.. tuple does not json-serialize, but is changed to list .. so change it back to tuplee
                # non-serializable parameters:
                dic = {
                    "parent"            : None,
                    "gpu_handler"       : self.gpu_handler,         # RootContainers(s) pass this downstream to child containers
                    "filterchain_group" : self.filterchain_group    # RootContainers(s) pass this downstream to child containers
                    }
                container_dic.update(dic)
                # now container has the parameters to instantiate the object
                print(">", container_dic)
                cont = container.VideoContainerNxM(**container_dic) # instantiate container
                cont.signals.closing.connect(self.rem_grid_container_slot)
                self.containers_grid.append(cont)
            
            if t == "PlayVideoContainerNxM":
                container_dic["child_class"] = nameToClass(container_dic.pop("child_class")) # swap from class name to class instance
                container_dic["geom"] = tuple(container_dic["geom"])  # woops.. tuple does not json-serialize, but is changed to list .. so change it back to tuplee
                # non-serializable parameters:
                dic = {
                    "parent"              : None,
                    "gpu_handler"         : self.gpu_handler,            # RootContainers(s) pass this downstream to child containers
                    "filterchain_group"   : self.filterchain_group_play,
                    "valkkafsmanager"     : self.valkkafsmanager,
                    "playback_controller" : self.playback_controller
                    }
                container_dic.update(dic)
                # now container has the parameters to instantiate the object
                print(">", container_dic)
                cont = container.PlayVideoContainerNxM(**container_dic) # instantiate container
                cont.signals.closing.connect(self.rem_playback_grid_container_slot)
                self.containers_playback.append(cont)
            
            elif t == "QMainWindow":
                geom = container_dic["geom"]
                self.setGeometry(geom[0], geom[1], geom[2], geom[3])
                
            elif t == "CameraListWindow":
                geom = container_dic["geom"]
                self.camera_list_win.setVisible(True)
                self.camera_list_win.setGeometry(geom[0], geom[1], geom[2], geom[3])

        
    def closeContainers(self):
        print("gui: closeContainers: containers_grid =", self.containers_grid)
        for container in self.containers_grid:
            container.close()
        self.containers_grid = []

        for container in self.containers_playback:
            container.close()
        self.containers_playback = []


    # *** Multiprocess handling ***

    def startProcesses(self):
        """Create and start python multiprocesses
        
        Starting a multiprocess creates a process fork.
        
        In theory, there should be no problem in first starting the multithreading environment and after that perform forks (only the thread requestin the fork is copied), but in practice, all kinds of weird behaviour arises.
        
        Read all about it in here : http://www.linuxprogrammingblog.com/threads-and-fork-think-twice-before-using-them
        """
        singleton.process_map = {} # each key is a list of started multiprocesses
        # self.process_avail = {} # count instances

        singleton.client_process_map = {}
        singleton.master_process_map = {}
        
        def span(mvision_classes: list, process_map: dict):
            for mvision_class in mvision_classes:
                name = mvision_class.name
                tag  = mvision_class.tag
                num  = mvision_class.max_instances
                if (tag not in process_map):
                    process_map[tag] = []
                    # self.process_avail[tag] = num
                    for n in range(0, num):
                        print("startProcesses: spanning", tag, n)
                        # verbose = True
                        verbose = singleton.mvision_verbose
                        p = mvision_class(verbose = verbose)
                        # p.start()
                        p.go()
                        process_map[tag].append(p)
            
        span(self.mvision_classes, singleton.process_map)
        span(self.mvision_client_classes, singleton.client_process_map)
        span(self.mvision_master_classes, singleton.master_process_map)
        
        
    def closeProcesses(self):

        def stop(process_map):
            for key in process_map:
                for p in process_map[key]:
                    # p.stop()
                    p.requestStop()

        def wait(process_map):
            for key in process_map:
                for p in process_map[key]:
                    p.waitStop()

        stop(singleton.process_map)
        stop(singleton.client_process_map)
        stop(singleton.master_process_map)

        wait(singleton.process_map)
        wait(singleton.client_process_map)
        wait(singleton.master_process_map)

        
    # *** Valkka ***
        
    def openValkka(self):
        self.cpu_scheme = CPUScheme()
        
        # singleton.data_model.camera_collection
        try:
            memory_config = next(singleton.data_model.config_collection.get({"classname" : MemoryConfigRow.__name__}))
        except StopIteration:
            print(pre, "Using default mem config")
            singleton.data_model.writeDefaultMemoryConfig()
            memory_config = default.get_memory_config()

        try:
            valkkafs_config = next(singleton.data_model.valkkafs_collection.get({"classname" : ValkkaFSConfigRow.__name__}))
        except StopIteration:
            print(pre, "Using default valkkafs config")
            singleton.data_model.writeDefaultValkkaFSConfig()
            valkkafs_config = default.get_valkkafs_config()

        n_frames = round(memory_config["msbuftime"] * default.fps / 1000.) # accumulated frames per buffering time = n_frames

        if (memory_config["bind"]):
            self.cpu_scheme = CPUScheme()
        else:
            self.cpu_scheme = CPUScheme(n_cores = -1)

        self.gpu_handler = GPUHandler(
            n_720p  = memory_config["n_720p"] * n_frames, # n_cameras * n_frames
            n_1080p = memory_config["n_1080p"] * n_frames,
            n_1440p = memory_config["n_1440p"] * n_frames,
            n_4K    = memory_config["n_4K"] * n_frames,
            msbuftime = memory_config["msbuftime"],
            verbose = False,
            cpu_scheme = self.cpu_scheme
        )

        self.livethread = LiveThread(
            name = "live_thread",
            verbose = False,
            affinity = self.cpu_scheme.getLive()
        )
        
        self.usbthread = USBDeviceThread(
            name = "usb_thread",
            verbose = False,
            affinity = self.cpu_scheme.getUSB()
        )

        # see datamodel.row.ValkkaFSConfigRow
        blocksize = valkkafs_config["blocksize"]
        n_blocks  = valkkafs_config["n_blocks"]
        fs_flavor = valkkafs_config["fs_flavor"] 
        record    = valkkafs_config["record"]

        # TODO: activate this if ValkkaFS changed in config!
        if fs_flavor == "file":
            partition_uuid = None
        else:
            partition_uuid = valkkafs_config["partition_uuid"]
            
        create_new_fs = False

        if self.valkkafs is None: # first time
            create_new_fs = False # try to load initially from disk
        else:
            print("openValkka: checking ValkkaFS")
            create_new_fs = not self.valkkafs.is_same( # has changed, so must recreate
                partition_uuid = partition_uuid, # None or a string
                blocksize = blocksize * 1024*1024,
                n_blocks = n_blocks
            )
            if create_new_fs: print("openValkka: ValkkaFS changed!")
        
        if not create_new_fs: # let's try to load it
            print("openValkka: trying to load ValkkaFS")
            try:
                self.valkkafs = ValkkaFS.loadFromDirectory(
                    dirname = singleton.valkkafs_dir.get()
                )
            except ValkkaFSLoadError as e:
                print("openValkka: loading ValkkaFS failed with", e)
                create_new_fs = True # no luck, must recreate

        if create_new_fs:
            print("openValkka: (re)create ValkkaFS")
            self.valkkafs = ValkkaFS.newFromDirectory(
                dirname = singleton.valkkafs_dir.get(),
                blocksize = valkkafs_config["blocksize"] * 1024*1024, # MB
                n_blocks = valkkafs_config["n_blocks"],
                partition_uuid = partition_uuid,
                verbose = True
            )
            # to keep things consistent..
            singleton.data_model.valkkafs_collection.new(
                ValkkaFSConfigRow,
                {
                    # "dirname"    : default.valkkafs_config["dirname"], # not written to db for the moment
                    "n_blocks"       : default.get_valkkafs_config()["n_blocks"],
                    "blocksize"      : valkkafs_config["blocksize"],
                    "fs_flavor"      : valkkafs_config["fs_flavor"],
                    "record"         : record,
                    "partition_uuid" : partition_uuid
                })


        """
        else:
            self.valkkafs = None
        """

        # if no recording selected, set self.valkkafsmanager = None
        self.valkkafsmanager = ValkkaFSManager(
            self.valkkafs,
            write = record, # True or False
            read = record,
            cache = record
            )
        self.playback_controller = PlaybackController(valkkafs_manager = self.valkkafsmanager)

        self.filterchain_group = LiveFilterChainGroup(
            datamodel     = singleton.data_model, 
            livethread    = self.livethread, 
            usbthread     = self.usbthread,
            gpu_handler   = self.gpu_handler, 
            cpu_scheme    = self.cpu_scheme)
        self.filterchain_group.read()

        if record:
            print("openValkka: ValkkaFS **RECORDING ACTIVATED**")
            self.filterchain_group.setRecording(RecordType.always, self.valkkafsmanager)
        
        # self.filterchain_group.update() # TODO: use this once fixed
        
        self.filterchain_group_play = PlaybackFilterChainGroup(
            datamodel     = singleton.data_model,
            valkkafsmanager
                        = self.valkkafsmanager,
            gpu_handler   = self.gpu_handler, 
            cpu_scheme    = self.cpu_scheme)
        self.filterchain_group_play.read()

        try:
            from valkka.mvision import multiprocess
        except ImportError:
            pass
        """
        else:
            if self.mvision:
                singleton.thread = multiprocess.QValkkaThread()
                singleton.thread.start()
        """

                
    def closeValkka(self):
        # live => chain => opengl
        #self.livethread.close()
        # self.usbthread.close()

        print("Closing live & usb threads")
        self.livethread.requestClose()
        self.usbthread.requestClose()
        self.livethread.waitClose()
        self.usbthread.waitClose()
        
        print("Closing filterchains")
        self.filterchain_group.close()
        self.filterchain_group_play.close()

        print("Closing OpenGLThreads")
        self.gpu_handler.close()

        self.playback_controller.close()
        
        print("Closing ValkkaFS threads")
        self.valkkafsmanager.close()
        
        # print("Closing multiprocessing frontend")
        """
        if singleton.thread:
            singleton.thread.stop()
        """
        

    def reOpenValkka(self):
        print("gui: valkka reinit")
        self.wait_window.show()
        self.saveWindowLayout()
        self.closeContainers()
        self.closeValkka()
        self.openValkka()
        self.loadWindowLayout()
        self.wait_window.hide()


    # *** slot generators ***

    def makeGridSlot(self, n, m):
        """Create a n x m video grid, show it and add it to the list of video containers
        """
        def slot_func():
            cont = container.VideoContainerNxM(
                gpu_handler         = self.gpu_handler, 
                filterchain_group   = self.filterchain_group, 
                n_dim               = n, 
                m_dim               = m
                )
            cont.signals.closing.connect(self.rem_grid_container_slot)
            self.containers_grid.append(cont)
            self.getMargins()
        setattr(self, "grid_%ix%i_slot" % (n, m), slot_func)


    def makePlaybackGridSlot(self, n, m):
        """Create a n x m video grid, show it and add it to the list of video containers
        """
        def slot_func():
            cont = container.PlayVideoContainerNxM(
                gpu_handler         = self.gpu_handler, 
                filterchain_group   = self.filterchain_group_play, 
                n_dim               = n, 
                m_dim               = m,
                valkkafsmanager     = self.valkkafsmanager,
                playback_controller = self.playback_controller
                )
            cont.signals.closing.connect(self.rem_playback_grid_container_slot)
            self.containers_playback.append(cont)
        setattr(self, "playback_grid_%ix%i_slot" % (n, m), slot_func)


    def makeMvisionSlot(self, cl):
        if cl.auto_menu == False:
            return
        def slot_func():
            if ( (cl.tag in singleton.process_map) and (len(singleton.process_map[cl.tag])>0) ):
                cont = container.VideoContainerNxM(
                    parent            = None,
                    gpu_handler       = self.gpu_handler,
                    filterchain_group = self.filterchain_group,
                    title             = cl.name,
                    n_dim             = 1,
                    m_dim             = 1,
                    child_class       = container.MVisionContainer,
                    child_class_pars  = {
                        "mvision_class": cl,
                        # "thread"       : singleton.thread,
                        # "process_map"  : singleton.process_map
                        }, 
                    )
                cont.signals.closing.connect(self.rem_grid_container_slot)
                self.containers_grid.append(cont)
            else:
                QtWidgets.QMessageBox.about(self,"Enough!","Can't instantiate more detectors of this type (max number is "+str(cl.max_instances)+")")     
        setattr(self, cl.name+"_slot", slot_func)


    def makeMvisionClientSlot(self, cl):
        if cl.auto_menu == False:
            return
        def slot_func():
            if ( (cl.tag in singleton.client_process_map) and len(singleton.client_process_map[cl.tag]) > 0 ):
                master_tag = cl.master
                if singleton.get_avail_master_process(master_tag) is not None:
                    cont = container.VideoContainerNxM(
                        parent            = None,
                        gpu_handler       = self.gpu_handler,
                        filterchain_group = self.filterchain_group,
                        title             = cl.name,
                        n_dim             = 1,
                        m_dim             = 1,
                        child_class       = container.MVisionClientContainer,
                        child_class_pars  = {
                            "mvision_class": cl,
                            # "thread"       : singleton.thread,
                            # "process_map"  : singleton.process_map
                            }, 
                        )
                    cont.signals.closing.connect(self.rem_grid_container_slot)
                    self.containers_grid.append(cont)
                else:
                    QtWidgets.QMessageBox.about(self,"Enough!","Can't instantiate more master processes for this detector")    
            else: 
                QtWidgets.QMessageBox.about(self,"Enough!","Can't instantiate more detectors of this type (max number is "+str(cl.max_instances)+")")     
        setattr(self, cl.name+"_slot", slot_func)


    # *** SLOTS ***

    # container related slots

    def rem_grid_container_slot(self, cont):
        print("gui: rem_grid_container_slot: removing container:",cont)
        print("gui: rem_grid_container_slot: containers:",self.containers_grid)
        try:
            self.containers_grid.remove(cont)
        except ValueError:
            print("gui: could not remove container",cont)
        print("gui: rem_grid_container_slot: containers now:", pformat(self.containers_grid))


    def rem_playback_grid_container_slot(self, cont):
        print("gui: rem_playback_grid_container_slot: removing container:",cont)
        print("gui: rem_playback_grid_container_slot: containers:",self.containers_playback)
        try:
            self.containers_playback.remove(cont)
        except ValueError:
            print("gui: could not remove container",cont)
        print("gui: rem_playback_grid_container_slot: containers now:", pformat(self.containers_playback))



        
    # explictly defined slot functions

    def exit_slot(self):
        self.close()

    def config_dialog_slot(self):
        self.config_modified = False
        self.valkkafs_modified = False
        self.config_win.show()
        self.manage_cameras_container.choose_first_slot()
        
    def config_modified_slot(self):
        self.config_modified = True
        
    def valkkafs_modified_slot(self):
        self.config_modified = True
        self.valkkafs_modified = True

    def camera_list_slot(self):
        self.camera_list_win.show()
    
    def config_dialog_close_slot(self):
        if (self.config_modified):
            self.updateCameraTree()
            self.reOpenValkka()
    
    def save_window_layout_slot(self):
        self.saveWindowLayout()

    def load_window_layout_slot(self):
        self.loadWindowLayout()

    def about_slot(self):
        QtWidgets.QMessageBox.about(self, "About", constant.program_info % (version.get(), version.getValkka()))
Пример #5
0
    def openValkka(self):
        self.cpu_scheme = CPUScheme()
        
        # singleton.data_model.camera_collection
        try:
            memory_config = next(singleton.data_model.config_collection.get({"classname" : MemoryConfigRow.__name__}))
        except StopIteration:
            print(pre, "Using default mem config")
            singleton.data_model.writeDefaultMemoryConfig()
            memory_config = default.get_memory_config()

        try:
            valkkafs_config = next(singleton.data_model.valkkafs_collection.get({"classname" : ValkkaFSConfigRow.__name__}))
        except StopIteration:
            print(pre, "Using default valkkafs config")
            singleton.data_model.writeDefaultValkkaFSConfig()
            valkkafs_config = default.get_valkkafs_config()

        n_frames = round(memory_config["msbuftime"] * default.fps / 1000.) # accumulated frames per buffering time = n_frames

        if (memory_config["bind"]):
            self.cpu_scheme = CPUScheme()
        else:
            self.cpu_scheme = CPUScheme(n_cores = -1)

        self.gpu_handler = GPUHandler(
            n_720p  = memory_config["n_720p"] * n_frames, # n_cameras * n_frames
            n_1080p = memory_config["n_1080p"] * n_frames,
            n_1440p = memory_config["n_1440p"] * n_frames,
            n_4K    = memory_config["n_4K"] * n_frames,
            msbuftime = memory_config["msbuftime"],
            verbose = False,
            cpu_scheme = self.cpu_scheme
        )

        self.livethread = LiveThread(
            name = "live_thread",
            verbose = False,
            affinity = self.cpu_scheme.getLive()
        )
        
        self.usbthread = USBDeviceThread(
            name = "usb_thread",
            verbose = False,
            affinity = self.cpu_scheme.getUSB()
        )

        # see datamodel.row.ValkkaFSConfigRow
        blocksize = valkkafs_config["blocksize"]
        n_blocks  = valkkafs_config["n_blocks"]
        fs_flavor = valkkafs_config["fs_flavor"] 
        record    = valkkafs_config["record"]

        # TODO: activate this if ValkkaFS changed in config!
        if fs_flavor == "file":
            partition_uuid = None
        else:
            partition_uuid = valkkafs_config["partition_uuid"]
            
        create_new_fs = False

        if self.valkkafs is None: # first time
            create_new_fs = False # try to load initially from disk
        else:
            print("openValkka: checking ValkkaFS")
            create_new_fs = not self.valkkafs.is_same( # has changed, so must recreate
                partition_uuid = partition_uuid, # None or a string
                blocksize = blocksize * 1024*1024,
                n_blocks = n_blocks
            )
            if create_new_fs: print("openValkka: ValkkaFS changed!")
        
        if not create_new_fs: # let's try to load it
            print("openValkka: trying to load ValkkaFS")
            try:
                self.valkkafs = ValkkaFS.loadFromDirectory(
                    dirname = singleton.valkkafs_dir.get()
                )
            except ValkkaFSLoadError as e:
                print("openValkka: loading ValkkaFS failed with", e)
                create_new_fs = True # no luck, must recreate

        if create_new_fs:
            print("openValkka: (re)create ValkkaFS")
            self.valkkafs = ValkkaFS.newFromDirectory(
                dirname = singleton.valkkafs_dir.get(),
                blocksize = valkkafs_config["blocksize"] * 1024*1024, # MB
                n_blocks = valkkafs_config["n_blocks"],
                partition_uuid = partition_uuid,
                verbose = True
            )
            # to keep things consistent..
            singleton.data_model.valkkafs_collection.new(
                ValkkaFSConfigRow,
                {
                    # "dirname"    : default.valkkafs_config["dirname"], # not written to db for the moment
                    "n_blocks"       : default.get_valkkafs_config()["n_blocks"],
                    "blocksize"      : valkkafs_config["blocksize"],
                    "fs_flavor"      : valkkafs_config["fs_flavor"],
                    "record"         : record,
                    "partition_uuid" : partition_uuid
                })


        """
        else:
            self.valkkafs = None
        """

        # if no recording selected, set self.valkkafsmanager = None
        self.valkkafsmanager = ValkkaFSManager(
            self.valkkafs,
            write = record, # True or False
            read = record,
            cache = record
            )
        self.playback_controller = PlaybackController(valkkafs_manager = self.valkkafsmanager)

        self.filterchain_group = LiveFilterChainGroup(
            datamodel     = singleton.data_model, 
            livethread    = self.livethread, 
            usbthread     = self.usbthread,
            gpu_handler   = self.gpu_handler, 
            cpu_scheme    = self.cpu_scheme)
        self.filterchain_group.read()

        if record:
            print("openValkka: ValkkaFS **RECORDING ACTIVATED**")
            self.filterchain_group.setRecording(RecordType.always, self.valkkafsmanager)
        
        # self.filterchain_group.update() # TODO: use this once fixed
        
        self.filterchain_group_play = PlaybackFilterChainGroup(
            datamodel     = singleton.data_model,
            valkkafsmanager
                        = self.valkkafsmanager,
            gpu_handler   = self.gpu_handler, 
            cpu_scheme    = self.cpu_scheme)
        self.filterchain_group_play.read()

        try:
            from valkka.mvision import multiprocess
        except ImportError:
            pass
        """
Пример #6
0
class MyGui(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MyGui, self).__init__()
        self.initVars()
        self.initConfigFiles()
        self.readDB()
        self.generateMethods()
        self.setupUi()
        self.startProcesses()
        self.openValkka()
        self.makeLogic()
        self.post()

    def initVars(self):
        self.thread = None  # a QThread that reads multiprocess pipes
        self.containers = []
        self.mvision_containers = []
        self.mvision_classes = tools.scanMVisionClasses()
        if (len(self.mvision_classes) > 0):
            self.mvision = True
        else:
            self.mvision = False

    def initConfigFiles(self):
        # self.config_file = tools.getConfigFile("config")
        self.version_file = tools.getConfigFile("version")
        self.first_start = True
        if (tools.hasConfigDir()
            ):  # this indicates that the program has been started earlier
            ver = self.readVersionNumber()
            print("valkka.live : loading config file for version number")
            if ver:
                if (ver[0] == version.VERSION_MAJOR
                        and ver[1] == version.VERSION_MINOR):
                    self.first_start = False
                else:  # incorrect version number
                    print("valkka.live : clearing config")
                    pass
                    # .. or handle migration somehow

        if self.first_start:  # first time program start
            # TODO: eula could be shown here
            print(pre, "initConfigFiles : first start")
            tools.makeConfigDir()
            self.saveVersionNumber()
            # self.saveConfigFile()
            self.save_window_layout()
            self.first_start = True

    def readDB(self):
        """Datamodel includes the following files: config.dat, devices.dat
        """
        self.dm = DataModel(directory=tools.getConfigDir())
        if (self.first_start):
            print(pre, "readDB : first start")
            self.dm.clearAll()
            self.dm.saveAll()

        # If camera collection is corrupt
        if not self.dm.checkCameraCollection():
            self.dm.clearCameraCollection()

    def generateMethods(self):
        """Generate some member functions
        """
        for i in range(1, 5):
            # adds member function grid_ixi_slot(self)
            self.make_grid_slot(i, i)

        for cl in self.mvision_classes:
            self.make_mvision_slot(cl)

    def QCapsulate(self, widget, name, blocking=False, nude=False):
        """Helper function that encapsulates QWidget into a QMainWindow
        """
        class QuickWindow(QtWidgets.QMainWindow):
            class Signals(QtCore.QObject):
                close = QtCore.Signal()
                show = QtCore.Signal()

            def __init__(self, blocking=False, parent=None, nude=False):
                super().__init__(parent)
                self.propagate = True  # send signals or not
                self.setStyleSheet(style.main_gui)
                if (blocking):
                    self.setWindowModality(QtCore.Qt.ApplicationModal)
                if (nude):
                    # http://doc.qt.io/qt-5/qt.html#WindowType-enum
                    # TODO: create a widget for a proper splashscreen (omitting X11 and centering manually)
                    # self.setWindowFlags(QtCore.Qt.Popup) # Qt 5.9+ : setFlags()
                    # self.setWindowFlags(QtCore.Qt.SplashScreen | QtCore.Qt.WindowStaysOnTopHint)
                    self.setWindowFlags(QtCore.Qt.Dialog)
                self.signals = self.Signals()

            def closeEvent(self, e):
                if (self.propagate):
                    self.signals.close.emit()
                e.accept()

            def showEvent(self, e):
                if (self.propagate):
                    self.signals.show.emit()
                e.accept()

            def setPropagate(self):
                self.propagate = True

            def unSetPropagate(self):
                self.propagate = False

        win = QuickWindow(blocking=blocking, nude=nude)
        win.setCentralWidget(widget)
        win.setLayout(QtWidgets.QHBoxLayout())
        win.setWindowTitle(name)
        return win

    def QTabCapsulate(self, name, widget_list, blocking=False):
        """Helper function that encapsulates QWidget into a QMainWindow
        
        :param widget_list:     List of tuples : [(widget,"name"), (widget,"name"), ..]
        
        """
        class QuickWindow(QtWidgets.QMainWindow):
            class Signals(QtCore.QObject):
                close = QtCore.Signal()
                show = QtCore.Signal()

            def __init__(self, blocking=False, parent=None):
                super().__init__(parent)
                self.propagate = True  # send signals or not
                self.setStyleSheet(style.main_gui)
                if (blocking):
                    self.setWindowModality(QtCore.Qt.ApplicationModal)
                self.signals = self.Signals()
                self.tab = QtWidgets.QTabWidget()
                self.setCentralWidget(self.tab)
                self.setLayout(QtWidgets.QHBoxLayout())

            def closeEvent(self, e):
                if (self.propagate):
                    self.signals.close.emit()
                e.accept()

            def showEvent(self, e):
                if (self.propagate):
                    self.signals.show.emit()
                e.accept()

            def setPropagate(self):
                self.propagate = True

            def unSetPropagate(self):
                self.propagate = False

        win = QuickWindow(blocking=blocking)
        win.setWindowTitle(name)
        for w in widget_list:
            win.tab.addTab(w[0], w[1])

        return win

    def setupUi(self):
        self.setStyleSheet(style.main_gui)
        self.setWindowTitle("Valkka Live")

        self.setGeometry(QtCore.QRect(100, 100, 500, 500))

        self.w = QtWidgets.QWidget(self)
        self.setCentralWidget(self.w)

        self.filemenu = FileMenu(parent=self)
        self.viewmenu = ViewMenu(parent=self)  # grids up to 4x4
        self.configmenu = ConfigMenu(parent=self)

        if self.mvision:
            mvision_elements = []

            for cl in self.mvision_classes:
                el = QuickMenuElement(title=cl.name, method_name=cl.name)
                mvision_elements.append(el)

            class MVisionMenu(QuickMenu):
                title = "Machine Vision"
                elements = mvision_elements

            self.mvisionmenu = MVisionMenu(parent=self)

        self.aboutmenu = AboutMenu(parent=self)

        # create container and their windows
        self.manage_cameras_container = self.dm.getDeviceListAndForm(None)
        #self.manage_cameras_win = self.QCapsulate(
        #   self.manage_cameras_container.widget, "Camera Configuration", blocking = True)

        self.manage_memory_container = self.dm.getConfigForm()
        #self.manage_memory_win = self.QCapsulate(
        #   self.manage_memory_container.widget, "Memory Configuration", blocking = True)

        # self.manage_memory_container.signals.save
        # self.manage_cameras_container.getForm().signals.save_record # ListAndForm : has a list and a formset (SlotFormSet).  SlotFormSet has the signals

        self.manage_memory_container.signals.save.connect(
            self.config_modified_slot)
        self.manage_cameras_container.getForm().signals.save_record.connect(
            self.config_modified_slot)

        self.config_win = self.QTabCapsulate(
            "Configuration",
            [(self.manage_cameras_container.widget, "Camera Configuration"),
             (self.manage_memory_container.widget, "Memory Configuration")])

        self.config_win.signals.close.connect(self.config_dialog_close_slot)
        # when the configuration dialog is reopened, inform the camera configuration form .. this way it can re-check if usb cams are available
        self.config_win.signals.show.connect(
            self.manage_cameras_container.getForm().show_slot)

        self.makeCameraTree()
        self.camera_list_win = self.QCapsulate(self.treelist, "Camera List")

        # self.camera_list_win.show()
        # self.treelist.show()

        # self.wait_widget = QtWidgets.QWidget()
        # self.wait_lay = QtWidgets.QHBoxLayout(self.wait_widget)
        # self.wait_label = QtWidgets.QLabel("Restarting Valkka, please wait ..", self.wait_widget)

        self.wait_label = QtWidgets.QLabel("Restarting Valkka, please wait ..")
        self.wait_window = self.QCapsulate(self.wait_label, "Wait", nude=True)
        # self.wait_window.show()

        # self.wait_window = QtWidgets.QMessageBox.information(None, "info","info")

    def makeCameraTree(self):
        self.root = HeaderListItem()
        self.treelist = BasicView(parent=None, root=self.root)
        self.updateCameraTree()

    def updateCameraTree(self):
        self.treelist.reset_()

        self.server = ServerListItem(name="Localhost",
                                     ip="127.0.0.1",
                                     parent=self.root)
        """
        self.server1 = ServerListItem(
            name="First Server", ip="192.168.1.20", parent=self.root)
        """
        """
        self.camera1 = RTSPCameraListItem(camera=RTSPCameraDevice(
            ip="192.168.1.4", username="******", password="******"), parent=self.server1)
        self.camera2 = RTSPCameraListItem(camera=RTSPCameraDevice(
            ip="192.168.1.4", username="******", password="******"), parent=self.server1)
        """
        devices = []

        for row in self.dm.camera_collection.get():
            # print(pre, "makeCameraTree : row", row)
            if (row["classname"] == DataModel.RTSPCameraRow.__name__):
                row.pop("classname")
                devices.append(
                    RTSPCameraListItem(
                        camera=DataModel.RTSPCameraDevice(**row),
                        parent=self.server))
            elif (row["classname"] == DataModel.USBCameraRow.__name__):
                row.pop("classname")
                devices.append(
                    USBCameraListItem(camera=DataModel.USBCameraDevice(**row),
                                      parent=self.server))

        self.treelist.update()
        self.treelist.expandAll()

    def makeLogic(self):
        # *** When camera list has been closed, re-create the cameralist tree and update filterchains ***
        # self.manage_cameras_win.signals.close.connect(self.updateCameraTree) # now put into save_camera_config_slot

        # self.manage_cameras_win.signals.close.connect(self.filterchain_group.update) # TODO: use this once fixed
        # self.manage_cameras_win.signals.close.connect(self.filterchain_group.read) # TODO: eh.. lets be sure of this .. (are we releasing slots in the LiveThread etc.)

        # self.manage_cameras_win.signals.close.connect(self.save_camera_config_slot)
        # self.manage_memory_container.signals.save.connect(self.save_memory_conf_slot)

        # *** Menu bar connections ***
        # the self.filemenu.exit attribute was autogenerated
        self.filemenu.exit.triggered.connect(self.exit_slot)
        self.filemenu.save_window_layout.triggered.connect(
            self.save_window_layout_slot)
        self.filemenu.load_window_layout.triggered.connect(
            self.load_window_layout_slot)
        """
        self.configmenu.manage_cameras.   triggered.connect(
            self.manage_cameras_slot)
        self.configmenu.memory_usage.     triggered.connect(
            self.memory_usage_slot)
        """

        self.configmenu.configuration_dialog.triggered.connect(
            self.config_dialog_slot)

        self.viewmenu.camera_list.triggered.connect(self.camera_list_slot)
        self.aboutmenu.about_valkka_live.triggered.connect(self.about_slot)

        # *** Connect autogenerated menu calls into autogenerated slot functions ***
        for i in range(1, 5):
            # gets member function grid_ixi_slot
            slot_func = getattr(self, "grid_%ix%i_slot" % (i, i))
            # gets member function grid_ixi from self.viewmenu.video_grid
            menu_func = getattr(self.viewmenu.video_grid,
                                "grid_%ix%i" % (i, i))
            menu_func.triggered.connect(slot_func)
            # i.e., like this : self.viewmenu.video_grid.grid_1x1.triggered.connect(slot_func)

        # *** autogenerated machine vision menu and slots ***
        for cl in self.mvision_classes:
            getattr(self.mvisionmenu,
                    cl.name).triggered.connect(getattr(self,
                                                       cl.name + "_slot"))

    def post(self):
        """
        self.mvision_container = container.VideoContainerNxM(
            parent            = None,
            gpu_handler       = self.gpu_handler,
            filterchain_group = self.filterchain_group,
            title             = "MVision",
            n_dim             = 1,
            m_dim             = 1,
            child_class       = container.MVisionContainer,
            child_class_pars  = mvision
            )
        """

    def serializeContainers(self):
        """Serializes the current view of open video grids (i.e. the view)
        """
        """ each serialized container looks like this:
        dic={# these are used when re-instantiating the view
            "classname"  : self.__class__.__name__,
            "kwargs"     : {}, # parameters that we're used to instantiate this class
            # these parameters are used by deserialize
            "x"          : self.window.x(),
            "y"          : self.window.y(),
            "width"      : self.window.width(),
            "height"     : self.window.height(),
            "streams"    : streams
            }
        """
        container_list = []
        mvision_container_list = []

        for container in self.containers:
            print("gui: serialize containers : container=", container)
            container_list.append(container.serialize())

        for container in self.mvision_containers:
            mvision_container_list.append(container.serialize())

        return {
            "container_list": container_list,
            "mvision_container_list": mvision_container_list
        }

    """
    def saveConfigFile(self):
        configdump = json.dumps({
            "containers": self.serializeContainers()
        })

        f = open(self.config_file, "w")
        f.write(configdump)
        f.close()
        self.saveVersionNumber()


    def loadConfigFile(self):
        try:
            f = open(self.config_file, "r")
        except FileNotFoundError:
            config = constant.config_skeleton
        else:
            config = json.loads(f.read())
        return config
    """

    def saveVersionNumber(self):
        f = open(self.version_file, "w")
        f.write(version.get())
        f.close()

    def readVersionNumber(self):
        try:
            f = open(self.version_file, "r")
            st = f.read()
            f.close()
            vs = []
            for s in st.split("."):
                vs.append(int(s))
        except:
            print("valkka.live : could not read version number")
            return None
        else:
            return vs

    def startProcesses(self):
        """Create and start python multiprocesses
        
        Starting a multiprocess creates a process fork.
        
        In theory, there should be no problem in first starting the multithreading environment and after that perform forks (only the thread requestin the fork is copied), but in practice, all kinds of weird behaviour arises.
        
        Read all about it in here : http://www.linuxprogrammingblog.com/threads-and-fork-think-twice-before-using-them
        """
        self.process_map = {}  # each key is a list of started multiprocesses
        # self.process_avail = {} # count instances

        for mvision_class in self.mvision_classes:
            name = mvision_class.name
            tag = mvision_class.tag
            num = mvision_class.max_instances
            if (tag not in self.process_map):
                self.process_map[tag] = []
                # self.process_avail[tag] = num
                for n in range(0, num):
                    p = mvision_class()
                    p.start()
                    self.process_map[tag].append(p)

    def closeProcesses(self):
        for key in self.process_map:
            for p in self.process_map[key]:
                p.stop()

    def openValkka(self):
        self.cpu_scheme = CPUScheme()

        # self.dm.camera_collection
        try:
            memory_config = next(
                self.dm.config_collection.get(
                    {"classname": DataModel.MemoryConfigRow.__name__}))
        except StopIteration:
            print(pre, "Using default mem config")
            memory_config = default.memory_config

        n_frames = round(
            memory_config["msbuftime"] * default.fps /
            1000.)  # accumulated frames per buffering time = n_frames

        if (memory_config["bind"]):
            self.cpu_scheme = CPUScheme()
        else:
            self.cpu_scheme = CPUScheme(n_cores=-1)

        self.gpu_handler = GPUHandler(
            n_720p=memory_config["n_720p"] * n_frames,  # n_cameras * n_frames
            n_1080p=memory_config["n_1080p"] * n_frames,
            n_1440p=memory_config["n_1440p"] * n_frames,
            n_4K=memory_config["n_4K"] * n_frames,
            msbuftime=memory_config["msbuftime"],
            verbose=False,
            cpu_scheme=self.cpu_scheme)

        self.livethread = LiveThread(name="live_thread",
                                     verbose=False,
                                     affinity=self.cpu_scheme.getLive())

        self.usbthread = USBDeviceThread(name="usb_thread",
                                         verbose=False,
                                         affinity=self.cpu_scheme.getUSB())

        self.filterchain_group = FilterChainGroup(datamodel=self.dm,
                                                  livethread=self.livethread,
                                                  usbthread=self.usbthread,
                                                  gpu_handler=self.gpu_handler,
                                                  cpu_scheme=self.cpu_scheme)
        self.filterchain_group.read()
        # self.filterchain_group.update() # TODO: use this once fixed

        try:
            from valkka.mvision import multiprocess
        except ImportError:
            pass
        else:
            if self.mvision:
                self.thread = multiprocess.QValkkaThread()
                self.thread.start()

    def closeValkka(self):
        # live => chain => opengl
        self.livethread.close()
        self.usbthread.close()
        self.filterchain_group.close()
        self.gpu_handler.close()
        if self.thread:
            self.thread.stop()

    def reOpenValkka(self):
        print("gui: valkka reinit")
        self.wait_window.show()
        self.save_window_layout("tmplayout")
        self.closeContainers()
        self.closeValkka()
        self.openValkka()
        self.load_window_layout("tmplayout")
        self.wait_window.hide()

    def closeContainers(self):
        print("gui: closeContainers: containers=", self.containers)
        print("gui: closeContainers: mvision containers=",
              self.mvision_containers)
        for container in self.containers:
            container.close()
        for container in self.mvision_containers:
            print("gui: closing mvision_container: ", container)
            container.close()
        self.containers = []
        self.mvision_containers = []

    def closeEvent(self, e):
        print("gui : closeEvent!")
        self.closeContainers()

        # self.manage_cameras_win.unSetPropagate() # don't send signals .. if you don't do this: close => closeEvent => will trigger self.reOpen
        # self.manage_cameras_win.close()

        self.camera_list_win.unSetPropagate()
        self.camera_list_win.close()

        self.config_win.unSetPropagate()
        self.config_win.close()

        self.closeValkka()
        self.dm.close()

        self.closeProcesses()
        e.accept()

    def rem_container_slot(self, cont):
        print("gui: rem_container_slot: removing container:", cont)
        print("gui: rem_container_slot: containers:", self.containers)
        try:
            self.containers.remove(cont)
        except ValueError:
            print("gui: could not remove container", cont)
        print("gui: rem_container_slot: containers now:", self.containers)

    def rem_mvision_container_slot(self, cont):
        print("gui: rem_mvision_container_slot: removing mvision container:",
              cont)
        print("gui: rem_mvision_container_slot: mvision containers:",
              self.mvision_containers)
        try:
            self.mvision_containers.remove(cont)
        except ValueError:
            print(
                "gui: rem_mvision_container_slot: could not remove container",
                cont)
        print("gui: rem_mvision_container_slot: mvision containers now:",
              self.mvision_containers)

    # slot function makers

    def make_grid_slot(self, n, m):
        """Create a n x m video grid, show it and add it to the list of video containers
        """
        def slot_func():
            cont = container.VideoContainerNxM(
                gpu_handler=self.gpu_handler,
                filterchain_group=self.filterchain_group,
                n_dim=n,
                m_dim=m)
            cont.signals.closing.connect(self.rem_container_slot)
            self.containers.append(cont)

        setattr(self, "grid_%ix%i_slot" % (n, m), slot_func)

    def make_mvision_slot(self, cl):
        def slot_func():
            if ((cl.tag in self.process_map)
                    and (len(self.process_map[cl.tag]) > 0)):
                cont = container.VideoContainerNxM(
                    parent=None,
                    gpu_handler=self.gpu_handler,
                    filterchain_group=self.filterchain_group,
                    title=cl.name,
                    n_dim=1,
                    m_dim=1,
                    child_class=container.MVisionContainer,
                    # serializable parameters (for re-creating this container):
                    child_class_pars={"mvision_class": cl},
                    # non-seriazable parameters:
                    child_class_pars_={
                        "thread": self.thread,
                        "process_map": self.process_map
                    })
                cont.signals.closing.connect(self.rem_mvision_container_slot)
                self.mvision_containers.append(cont)
            else:
                QtWidgets.QMessageBox.about(
                    self, "Enough!",
                    "Can't instantiate more detectors of this type (max number is "
                    + str(cl.max_instances) + ")")

        setattr(self, cl.name + "_slot", slot_func)

    def save_window_layout(self, filename="layout"):
        container_dic = self.serializeContainers()
        print(pre, "save_window_layout : container_dic =", container_dic)
        # f = open(tools.getConfigFile(filename), "w")
        # f.write(json.dumps(container_list))
        f = open(tools.getConfigFile(filename), "wb")
        f.write(pickle.dumps(container_dic))
        f.close()

    def load_window_layout(self, filename="layout"):
        self.closeContainers()

        # f = open(tools.getConfigFile(filename), "r")
        # container_list = json.loads(f.read())
        f = open(tools.getConfigFile(filename), "rb")
        container_dic = pickle.loads(f.read())
        f.close()
        print("load_window_layout: container_dic: ", container_dic)
        namespace = container.__dict__
        # devices_by_id = self.dm.getDevicesById({"classname" : DataModel.RTSPCameraRow.__name__})
        devices_by_id = self.dm.getDevicesById()

        for cont in container_dic["container_list"]:
            classname = cont["classname"]
            kwargs = cont["kwargs"]
            kwargs["gpu_handler"] = self.gpu_handler
            kwargs["filterchain_group"] = self.filterchain_group
            class_instance = namespace[classname]

            container_instance = class_instance(**
                                                kwargs)  # create the container
            # move it to the right position
            container_instance.deSerialize(cont, devices_by_id)
            self.containers.append(container_instance)

        for cont in container_dic["mvision_container_list"]:
            print("\nload_window_layout: mvision:", cont)

    # explictly defined slot functions

    def exit_slot(self):
        self.close()

    """
    def manage_cameras_slot(self):
        self.manage_cameras_win.show()

    def memory_usage_slot(self):
        self.manage_memory_win.show()
    """

    def config_dialog_slot(self):
        self.config_modified = False
        self.config_win.show()
        self.manage_cameras_container.choose_first_slot()

    def config_modified_slot(self):
        self.config_modified = True

    def camera_list_slot(self):
        self.camera_list_win.show()

    """
    def save_memory_conf_slot(self):
        self.manage_memory_win.close()
        self.reOpenValkka()

    def save_camera_config_slot(self):
        self.updateCameraTree()
        self.reOpenValkka()
    """

    def config_dialog_close_slot(self):
        if (self.config_modified):
            self.updateCameraTree()
            self.reOpenValkka()

    def save_window_layout_slot(self):
        self.save_window_layout()

    def load_window_layout_slot(self):
        self.load_window_layout()

    def about_slot(self):
        QtWidgets.QMessageBox.about(
            self, "About",
            constant.program_info % (version.get(), version.getValkka()))
Пример #7
0
    def openValkka(self):
        self.cpu_scheme = CPUScheme()

        # singleton.data_model.camera_collection
        try:
            memory_config = next(
                singleton.data_model.config_collection.get(
                    {"classname": MemoryConfigRow.__name__}))
        except StopIteration:
            print(pre, "Using default mem config")
            singleton.data_model.writeDefaultMemoryConfig()
            memory_config = default.get_memory_config()

        try:
            valkkafs_config = next(
                singleton.data_model.valkkafs_collection.get(
                    {"classname": ValkkaFSConfigRow.__name__}))
        except StopIteration:
            print(pre, "Using default valkkafs config")
            singleton.data_model.writeDefaultValkkaFSConfig()
            valkkafs_config = default.get_valkkafs_config()

        n_frames = round(
            memory_config["msbuftime"] * default.fps /
            1000.)  # accumulated frames per buffering time = n_frames

        if (memory_config["bind"]):
            self.cpu_scheme = CPUScheme()
        else:
            self.cpu_scheme = CPUScheme(n_cores=-1)

        self.gpu_handler = GPUHandler(
            n_720p=memory_config["n_720p"] * n_frames,  # n_cameras * n_frames
            n_1080p=memory_config["n_1080p"] * n_frames,
            n_1440p=memory_config["n_1440p"] * n_frames,
            n_4K=memory_config["n_4K"] * n_frames,
            msbuftime=memory_config["msbuftime"],
            verbose=False,
            cpu_scheme=self.cpu_scheme)

        self.livethread = LiveThread(name="live_thread",
                                     verbose=False,
                                     affinity=self.cpu_scheme.getLive())

        self.usbthread = USBDeviceThread(name="usb_thread",
                                         verbose=False,
                                         affinity=self.cpu_scheme.getUSB())

        # see datamodel.row.ValkkaFSConfigRow
        blocksize = valkkafs_config["blocksize"]
        n_blocks = valkkafs_config["n_blocks"]
        #fs_flavor = valkkafs_config["fs_flavor"]
        #record    = valkkafs_config["record"]

        self.filterchain_group = LiveFilterChainGroup(
            datamodel=singleton.data_model,
            livethread=self.livethread,
            usbthread=self.usbthread,
            gpu_handler=self.gpu_handler,
            cpu_scheme=self.cpu_scheme)
        self.filterchain_group.read()

        # TODO: RecordType..?
        if singleton.use_playback:
            print("openValkka: ValkkaFS **PLAYBACK & RECORDING ACTIVATED**")
            # ValkkaSingleFSHandler:
            # directory handling and valkkafs <-> stream id association
            self.valkka_fs_handler = ValkkaSingleFSHandler(
                basedir=singleton.valkkafs_dir.get(),
                blocksize=blocksize * 1024 * 1024,  # MB
                n_blocks=n_blocks)
            if self.valkkafs_modified:
                print("openValkka: removing all recorded streams")
                self.valkka_fs_handler.clear()
                self.valkka_fs_handler.wipe()

            for row in singleton.data_model.camera_collection.get():
                _id = row["_id"]  # get stream id
                slot = row["slot"]
                classname = row["classname"]
                if classname != "EmptyRow":
                    # print(">", row)
                    self.valkka_fs_handler.load(_id)
                    # ..creates new valkka if doesn't exist
            self.valkkafsmanager = ValkkaFSManager(
                self.valkka_fs_handler.tolist())
            self.valkkafsmanager.start()
            #self.filterchain_group.setRecording(RecordType.always, self.valkkafsmanager)# OLD
            # self.filterchain_group: source for live stream
            # self.filterchain_group_play: sink where the playback/saved stream should
            # be sent
            self.filterchain_group_play = PlaybackFilterChainGroup(
                datamodel=singleton.data_model,
                gpu_handler=self.gpu_handler,
                cpu_scheme=self.cpu_scheme)
            self.filterchain_group_play.read()
            # print("openValkka: self.filterchain_group_play: len=", len(self.filterchain_group_play))
            # connect live & playback filterchains with the manager
            for valkkafs, inputfilter in self.valkkafsmanager.iterateFsInput():
                _id = self.valkka_fs_handler.getId(valkkafs)
                if _id is None:
                    print("WARNING: main: could not get id for", valkkafs)
                    continue
                playback_fc = self.filterchain_group_play.get(_id=_id)
                if playback_fc is None:
                    print("WARNING: main: could not find _id", _id,\
                        "in playback filterchain group")
                    """
                    for chain in self.filterchain_group_play.chains:
                        print(">>", chain)
                        for key, getter in chain.iterateGetters():
                            print(">", key, getter())
                    """
                    continue
                live_fc = self.filterchain_group.get(_id=_id)
                if live_fc is None:
                    print("WARNING: main: could not find _id", _id,\
                        "in live filterchain group")
                    continue
                self.valkkafsmanager.map_(
                    valkkafs=valkkafs,
                    # read & cached stream is sent/output'd here:
                    framefilter=playback_fc.getInputFilter(),
                    write_slot=live_fc.slot,
                    read_slot=playback_fc.slot,
                    _id=_id)
                # frames coming from the live stream are sent to
                # valkkafsmanager's correct inputfilter
                # (there is one corresponding to each valkkafs)
                live_fc.connectRecTo(inputfilter, RecordType.always)
                # TODO: disconnect at exit..?

            # self.filterchain_group.update() # TODO: use this once fixed
            self.playback_controller = PlaybackController(
                valkkafs_manager=self.valkkafsmanager)