Exemple #1
0
    def __init__(self, aw):
        self.aw = aw

        self.readRetries = 1
        self.channels = 10  # maximal number of S7 channels
        self.host = '127.0.0.1'  # the TCP host
        self.port = 102  # the TCP port
        self.rack = 0  # 0,..,7
        self.slot = 0  # 0,..,31

        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider S/ actions with "read" command

        self.area = [0] * self.channels
        self.db_nr = [1] * self.channels
        self.start = [0] * self.channels
        self.type = [
            0
        ] * self.channels  # type 0 => int, type 1 => float, type 2 => intFloat
        #  type 3 => Bool(0), type 4 => Bool(1), type 5 => Bool(2), type 6 => Bool(3), type 7 => Bool(4), type 8 => Bool(5), type 9 => Bool(6), type 10 => Bool(7)
        self.mode = [
            0
        ] * self.channels  # temp mode is an int here, 0:__,1:C,2:F (this is different than other places)
        self.div = [0] * self.channels

        self.optimizer = True  # if set, values of consecutive register addresses are requested in single requests
        self.fetch_max_blocks = False  # if set, the optimizer fetches only one sequence per area from the minimum to the maximum register ignoring gaps
        # S7 areas associated to dicts associating S7 DB numbers to start registers in use
        # for optimized read of full register segments with single requests
        # this dict is re-computed on each connect() by a call to updateActiveRegisters()
        # NOTE: for registers of type float (32bit = 2x16bit) also the succeeding register is registered here
        self.activeRegisters = {}
        # the readings cache that is filled by requesting sequences of values in blocks
        self.readingsCache = {}

        self.PID_area = 0
        self.PID_db_nr = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""
        self.PIDmultiplier = 0
        self.SVtype = 0
        self.SVmultiplier = 0

        self.COMsemaphore = QSemaphore(1)

        self.areas = [
            0x81,  # PE
            0x82,  # PA
            0x83,  # MK
            0x1C,  # CT
            0x1D,  # TM
            0x84,  # DB
        ]

        self.plc = None
        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data
        self.libLoaded = False
Exemple #2
0
    def __init__(self, sendmessage, adderror, addserial, aw):
        self.sendmessage = sendmessage  # function to create an Artisan message to the user in the message line
        self.adderror = adderror  # signal an error to the user
        self.addserial = addserial  # add to serial log
        self.aw = aw

        # retries
        self.readRetries = 1
        #default initial settings. They are changed by settingsload() at initiation of program acording to the device chosen
        self.comport = "COM5"  #NOTE: this string should not be translated.
        self.baudrate = 115200
        self.bytesize = 8
        self.parity = 'N'
        self.stopbits = 1
        self.timeout = 1.0
        self.PID_slave_ID = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""

        self.channels = 8
        self.inputSlaves = [0] * self.channels
        self.inputRegisters = [0] * self.channels
        self.inputFloats = [False] * self.channels
        self.inputBCDs = [False] * self.channels
        self.inputCodes = [3] * self.channels
        self.inputDivs = [0] * self.channels  # 0: none, 1: 1/10, 2:1/100
        self.inputModes = ["C"] * self.channels

        self.optimizer = True  # if set, values of consecutive register addresses are requested in single requests
        # MODBUS functions associated to dicts associating MODBUS slave ids to registers in use
        # for optimized read of full register segments with single requests
        # this dict is re-computed on each connect() by a call to updateActiveRegisters()
        # NOTE: for registers of type float and BCD (32bit = 2x16bit) also the succeeding register is registered here
        self.activeRegisters = {}
        # the readings cache that is filled by requesting sequences of values in blocks
        self.readingsCache = {}

        self.SVmultiplier = 0
        self.PIDmultiplier = 0
        self.byteorderLittle = False
        self.wordorderLittle = True
        self.master = None
        self.COMsemaphore = QSemaphore(1)
        self.host = '127.0.0.1'  # the TCP/UDP host
        self.port = 502  # the TCP/UDP port
        self.type = 0
        # type =
        #    0: Serial RTU
        #    1: Serial ASCII
        #    2: Serial Binary
        #    3: TCP
        #    4: UDP
        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider Modbus actions with "read" command

        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data
Exemple #3
0
class Database:
    def __init__(self, dbfilename):
        try:
            self.name = dbfilename
            self.dbsemaphore = QSemaphore(
                1)  # to control concurrent write access to db
            self.engine = create_engine('sqlite:///{dbFileName}'.format(
                dbFileName=dbfilename))  #, poolclass=SingletonThreadPool)
            self.session = scoped_session(sessionmaker())
            self.session.configure(bind=self.engine, autoflush=False)
            self.metadata = Base.metadata
            self.metadata.create_all(self.engine)
            self.metadata.echo = True
            self.metadata.bind = self.engine
        except Exception as e:
            log.info('Could not create database. Please try again.')
            log.info(e)

    def openDB(self, dbfilename):
        try:
            self.name = dbfilename
            self.dbsemaphore = QSemaphore(
                1)  # to control concurrent write access to db
            self.engine = create_engine('sqlite:///{dbFileName}'.format(
                dbFileName=dbfilename))  #, poolclass=SingletonThreadPool)
            self.session = scoped_session(sessionmaker())
            self.session.configure(bind=self.engine, autoflush=False)
            self.metadata = Base.metadata
            self.metadata.create_all(self.engine)
            self.metadata.echo = True
            self.metadata.bind = self.engine
        except:
            log.info('Could not open database file. Is the file corrupted?')

    def commit(self):
        self.dbsemaphore.acquire()
        log.info("DB lock aquired")
        try:
            session = self.session()
            rnd = float(randint(1, 99)) / 100.00
            log.info("Waiting {0}s before commit...".format(str(rnd)))
            time.sleep(rnd)
            session.commit()
        except Exception as e:
            log.error("DB Commit issue")
            log.error(str(e))
            try:
                rnd = float(randint(1, 99)) / 100.00
                time.sleep(rnd)
                log.info("Waiting {0}s before commit...".format(str(rnd)))
                session.commit()
            except Exception as e:
                log.error("DB Commit issue on retry")
                log.error(str(e))
                pass
        self.dbsemaphore.release()
        log.info("DB lock released")
Exemple #4
0
class Database:
    def __init__(self, dbfilename):
        from db.database import Base
        self.log = getDbLogger()
        self.base = Base
        try:
            self.establishSqliteConnection(dbfilename)
        except Exception as e:
            self.log.error('Could not create SQLite database. Please try again.')
            self.log.info(e)

    def openDB(self, dbfilename):
        try:
            self.establishSqliteConnection(dbfilename)
        except:
            self.log.error('Could not open SQLite database file. Is the file corrupted?')

    def establishSqliteConnection(self, dbFileName: str):
        self.name = dbFileName
        self.dbsemaphore = QSemaphore(1)  # to control concurrent write access to db
        self.engine = create_engine(
            'sqlite:///{dbFileName}'.format(dbFileName=dbFileName))
        self.session = scoped_session(sessionmaker())
        self.session.configure(bind=self.engine, autoflush=False)
        self.metadata = self.base.metadata
        self.metadata.create_all(self.engine)
        self.metadata.echo = True
        self.metadata.bind = self.engine
        self.log.info(f"Established SQLite connection on file '{dbFileName}'")

    def commit(self):
        self.dbsemaphore.acquire()
        self.log.debug("DB lock acquired")
        try:
            session = self.session()
            rnd = float(randint(1, 99)) / 100.00
            self.log.debug("Waiting {0}s before commit...".format(str(rnd)))
            time.sleep(rnd)
            session.commit()
        except Exception as e:
            self.log.error("DB Commit issue")
            self.log.error(str(e))
            try:
                rnd = float(randint(1, 99)) / 100.00
                time.sleep(rnd)
                self.log.debug("Waiting {0}s before commit...".format(str(rnd)))
                session.commit()
            except Exception as e:
                self.log.error("DB Commit issue on retry")
                self.log.error(str(e))
                pass
        self.dbsemaphore.release()
        self.log.debug("DB lock released")
Exemple #5
0
 def establishSqliteConnection(self, dbFileName: str):
     self.name = dbFileName
     self.dbsemaphore = QSemaphore(
         1)  # to control concurrent write access to db
     self.engine = create_engine(
         'sqlite:///{dbFileName}'.format(dbFileName=dbFileName))
     self.session = scoped_session(sessionmaker())
     self.session.configure(bind=self.engine, autoflush=False)
     self.metadata = Base.metadata
     self.metadata.create_all(self.engine)
     self.metadata.echo = True
     self.metadata.bind = self.engine
Exemple #6
0
 def connect(self, dbfilename):
     self.name = dbfilename
     self.dbsemaphore = QSemaphore(
         1)  # to control concurrent write access to db
     self.engine = create_engine('sqlite:///' + dbfilename,
                                 connect_args={"check_same_thread": False})
     self.session = scoped_session(sessionmaker())
     self.session.configure(bind=self.engine, autoflush=False)
     self.metadata = Base.metadata
     self.metadata.create_all(self.engine)
     self.metadata.echo = True
     self.metadata.bind = self.engine
 def delay_method_call(self, *args, **kwargs):
     if self.thread() != QThread.currentThread():
         semaphore = QSemaphore()
     else:
         semaphore = None
     event = QueuedCallEvent(method, (self, ) + args, kwargs, semaphore)
     QCoreApplication.postEvent(self, event)
     if semaphore is None:
         QCoreApplication.sendPostedEvents()
     else:
         # Wait until the other thread's event loop processes the event
         semaphore.acquire()
     return event.result()
Exemple #8
0
 def openDB(self, dbfilename):
     try:
         self.name = dbfilename
         self.dbsemaphore = QSemaphore(1)                            # to control concurrent write access to db
         self.engine = create_engine('sqlite:///{dbFileName}'.format(dbFileName = dbfilename)) #, poolclass=SingletonThreadPool)
         self.session = scoped_session(sessionmaker())
         self.session.configure(bind = self.engine, autoflush=False)
         self.metadata = Base.metadata
         self.metadata.create_all(self.engine)
         self.metadata.echo = True
         self.metadata.bind = self.engine
     except:
         log.info('Could not open database file. Is the file corrupted?')
Exemple #9
0
 def __init__(self):
     # a dictionary associating all physical attached Phidget channels
     # to their availablility state:
     #    True: available for attach to a software channel
     #    False: occupied and connected to a software channel
     # access to this dict is protected by the managersemaphore and
     # should happen only via the methods addChannel and deleteChannel
     self.attachedPhidgetChannels = {}
     self.managersemaphore = QSemaphore(1)
     self.manager = Manager()
     self.manager.setOnAttachHandler(self.attachHandler)
     self.manager.setOnDetachHandler(self.detachHandler)
     self.manager.open()
    def __init__(self, parent=None):
        super(MyWindow, self).__init__(parent)
        self.init_Ui()
        self.com = Common()
        self.title = [
            u"COUNT", u"CPU(%)", u"MEM(M)", u"FPS", u"Wifi下行(KB/S)",
            u"Wifi上行(KB/S)", u"下载流量(MB)", u"上传流量(MB)", u"Wifi总流量(MB)",
            u"移动网络下行(KB/S)", u"移动网络上行(KB/S)", u"下载流量(MB)", u"上传流量(MB)",
            u"移动网络总流量(MB)", u"温度", "Drawcall", u"电量"
        ]
        self.excel_path = "D:\PerfReport"
        # self.create_excel()
        self.getData = 0

        BufferSize = 1  #同时并发的线程数
        FpsS = QSemaphore(BufferSize)  # cpu并发锁
        CpuS = QSemaphore(0)
        DrawcallS = QSemaphore(0)
        NetS = QSemaphore(0)
        MemS = QSemaphore(0)
        TempS = QSemaphore(0)
        BatteryS = QSemaphore(0)
        TimeS = QSemaphore(0)
        self.lock = {}  #并发锁词典
        self.lock['cpu'] = CpuS
        self.lock['fps'] = FpsS
        self.lock['drawcall'] = DrawcallS
        self.lock['net'] = NetS
        self.lock['mem'] = MemS
        self.lock['temp'] = TempS
        self.lock['battery'] = BatteryS
        self.lock['time'] = TimeS
        self.dw = Draw()
        self.isCollect = 0
Exemple #11
0
    def _play(self):
        #Input values
        start = float(self.startInput.text())
        stop = float(self.stopInput.text())

        #If state is PAUSE skip these steps
        if self.state != "PAUSE":
            self.timePosition = start

            #Iterator preparation
            sampleRate = self.eegSettings["sampleRate"]
            iterStep = int(round(sampleRate * self.simDelay))
            iterStart = int(start * sampleRate)
            iterStop = int(stop * sampleRate)
            #simDelay correction for int aproximation
            self.simDelay = iterStep / sampleRate

            self.iterator = iter(self.helper[iterStart:iterStop:iterStep])

            #Next iteration to test if values are correct
            try:
                next(self.iterator)
            except StopIteration:
                QtWidgets.QMessageBox.warning(
                    self, "Error", "The start and stop points are too close",
                    QtWidgets.QMessageBox.Ok)
                return
            #Initialize animations of windows
            for window in self.windowList:
                window.initAnimation(start)

        #Init semaphore and loopTrigger
        self.semaphore = QSemaphore(0)
        loop = LoopTrigger(self.semaphore, minDelay=self.rtDelay)
        self.loop = loop

        #Connect signals
        loop.sigUpdate.connect(self.__updateFields)
        loop.sigUpdate.connect(self.__playAnimation)

        self.stopSignal.connect(loop.stop)

        #Init thread
        self.loopThread = QThread()
        loop.moveToThread(self.loopThread)
        self.loopThread.started.connect(loop.loop)
        self.loopThread.start()

        #Set new state
        self.__setState("PLAY")
Exemple #12
0
 def __init__(self, dbfilename):
     try:
         self.name = dbfilename
         self.dbsemaphore = QSemaphore(1)                            # to control concurrent write access to db
         self.engine = create_engine('sqlite:///{dbFileName}'.format(dbFileName = dbfilename)) #, poolclass=SingletonThreadPool)
         self.session = scoped_session(sessionmaker())
         self.session.configure(bind = self.engine, autoflush=False)
         self.metadata = Base.metadata
         self.metadata.create_all(self.engine)
         self.metadata.echo = True
         self.metadata.bind = self.engine
     except Exception as e:
         log.info('Could not create database. Please try again.')
         log.info(e)
Exemple #13
0
class Database:
    def __init__(self, dbfilename):

        try:
            self.connect(dbfilename)
            #setup_all()
            #create_all()

        except Exception as e:
            print('[-] Could not create database. Please try again.')
            print(e)

    def openDB(self, dbfilename):

        try:
            self.connect(dbfilename)
            #setup_all()

        except Exception as e:
            print('[-] Could not open database file. Is the file corrupted?')
            print(e)

    def connect(self, dbfilename):
        self.name = dbfilename
        self.dbsemaphore = QSemaphore(
            1)  # to control concurrent write access to db
        self.engine = create_engine('sqlite:///' + dbfilename,
                                    connect_args={"check_same_thread": False})
        self.session = scoped_session(sessionmaker())
        self.session.configure(bind=self.engine, autoflush=False)
        self.metadata = Base.metadata
        self.metadata.create_all(self.engine)
        self.metadata.echo = True
        self.metadata.bind = self.engine

    # this function commits any modified data to the db, ensuring no concurrent write access to the DB (within the same thread)
    # if you code a thread that writes to the DB, make sure you acquire/release at the beginning/end of the thread (see nmap importer)
    def commit(self):
        self.dbsemaphore.acquire()

        try:
            session = self.session
            session.commit()

        except Exception as e:
            print("[-] Could not commit to DB.")
            print(e)

        self.dbsemaphore.release()
Exemple #14
0
 def __init__(self, buffer_size=5):
     self.buffer_size = buffer_size
     self.free_slots = QSemaphore(self.buffer_size)
     self.used_slots = QSemaphore(0)
     self.clear_buffer_add = QSemaphore(1)
     self.clear_buffer_get = QSemaphore(1)
     self.queue_mutex = QMutex()
     self.queue = Queue(self.buffer_size)
Exemple #15
0
    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.sem_for_timer = QSemaphore()

        self.reader_Thread = QThread()
        self.reader = ConsoleReader(self.sem_for_timer,
                                    self.semaphore_start_reading)
        self.reader.moveToThread(self.reader_Thread)

        # self.timerThread = QThread()
        # self.timer = Timer(self.sem_for_timer, self.reader_Thread)
        # self.timer.moveToThread(self.timerThread)

        self.writer_Thread = QThread()
        self.writer = ConsoleWriter(self.semaphore_start_reading)
        self.writer.moveToThread(self.writer_Thread)

        self.thr_read = Thread(target=self.ff)
        self.thr_write = Thread(target=self.ww)
        # self.thr_timer = Thread(target=self.gg)

        self.ui.startBt.clicked.connect(self.startProcess)
        # self.writer.process_created.connect(self.pp)
        # self.writer.process_created.connect(self.reader.set_pipe)
        self.ui.sendBt.clicked.connect(self.addInput)
        self.writer.value_entered.connect(self.semaphore_start_reading.release)
        self.reader.readed.connect(self.addToConsole)
        # self.timer.timeout.connect(self.newThread)

        self.writer_Thread.start()
        # self.timerThread.start()
        self.reader_Thread.start()
Exemple #16
0
    def __init__(self, sendmessage, adderror, addserial):
        self.sendmessage = sendmessage  # function to create an Artisan message to the user in the message line
        self.adderror = adderror  # signal an error to the user
        self.addserial = addserial  # add to serial log

        self.readRetries = 1
        self.channels = 8  # maximal number of S7 channels
        self.host = '127.0.0.1'  # the TCP host
        self.port = 102  # the TCP port
        self.rack = 0  # 0,..,7
        self.slot = 0  # 0,..,31

        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider S/ actions with "read" command

        self.area = [0] * self.channels
        self.db_nr = [1] * self.channels
        self.start = [0] * self.channels
        self.type = [0] * self.channels
        self.mode = [
            0
        ] * self.channels  # temp mode is an int here, 0:__,1:C,2:F (this is different than other places)
        self.div = [0] * self.channels

        self.PID_area = 0
        self.PID_db_nr = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""
        self.SVmultiplier = 0
        self.PIDmultiplier = 0

        self.COMsemaphore = QSemaphore(1)

        self.areas = [
            0x81,  # PE
            0x82,  # PA
            0x83,  # MK
            0x1C,  # CT
            0x1D,  # TM
            0x84,  # DB
        ]

        self.plc = None
        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data
 def __init__(self, self_id, message_manager, election_time=5):
     super().__init__()
     self.id = self_id
     self.message_manager = message_manager
     self.semaphore = QSemaphore(1)
     self.self_elected = True
     self.election_timer = election_time
     self.message_manager.signal_candidate_found.connect(
         self.candidate_found)
 def __init__(self, size):
     # Save buffer size
     self.bufferSize = size
     # Create semaphores
     self.freeSlots = QSemaphore(self.bufferSize)
     self.usedSlots = QSemaphore(0)
     self.clearBuffer_add = QSemaphore(1)
     self.clearBuffer_get = QSemaphore(1)
     # Create mutex
     self.queueProtect = QMutex()
     # Create queue
     self.queue = Queue(self.bufferSize)
Exemple #19
0
class Window(QMainWindow):
    semaphore = QSemaphore()
    input_sended = pyqtSignal()
    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.input = None
        # self.thr = Thread().
        self.timer = QTimer()
        self.reader_Thread = QThread()
        self.reader = ConsoleReader(self.semaphore, self.timer)
        self.reader.moveToThread(self.reader_Thread)

        self.writer_Thread = QThread()
        self.writer = ConsoleWriter(self.semaphore)
        self.writer.moveToThread(self.writer_Thread)

        self.ui.startBt.clicked.connect(self.startProcess)
        self.writer.process_created.connect(self.pp)
        # self.writer.process_created.connect(self.reader.set_pipe)
        self.reader.readed.connect(self.addToConsole)
        self.ui.sendBt.clicked.connect(self.addInput)
        # self.timer.timeout.connect(self.ii)
        # self.timer.timeout.connect(self.timer.stop)

        self.reader_Thread.start()
        self.writer_Thread.start()


    def ii(self):
        print('stopped')
        # self.reader_Thread.terminate()
        self.reader.thread().wait()

    def pp(self, process):
        self.reader.set_pipe(process)

    def addToConsole(self, output):
        self.ui.outputText.append(output)

    def startProcess(self):
        path = self.ui.comandLine.text()
        if '\\' in path:
            path = path.replace('\\', '/')
            # print(path)
        self.writer.createProcess(path)
        self.reader.run()

    def addInput(self):
        input = self.ui.inputText.text()
        self.writer.write(input)
        self.ui.outputText.append('>'+input)
Exemple #20
0
 def read(self, *data):
     id = self.__getRandomId()
     sem = QSemaphore(1)
     sem.acquire()
     self.queue.put({'type': 'read', 'data': list(data), 'id': id, 'sem': sem})
     # wait for release the semaphore
     sem.acquire()
     state = self.ids[id]
     del self.ids[id]
     return state
Exemple #21
0
class s7port(object):
    def __init__(self, aw):
        self.aw = aw

        self.readRetries = 1
        self.channels = 10  # maximal number of S7 channels
        self.host = '127.0.0.1'  # the TCP host
        self.port = 102  # the TCP port
        self.rack = 0  # 0,..,7
        self.slot = 0  # 0,..,31

        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider S/ actions with "read" command

        self.area = [0] * self.channels
        self.db_nr = [1] * self.channels
        self.start = [0] * self.channels
        self.type = [
            0
        ] * self.channels  # type 0 => int, type 1 => float, type 2 => intFloat
        #  type 3 => Bool(0), type 4 => Bool(1), type 5 => Bool(2), type 6 => Bool(3), type 7 => Bool(4), type 8 => Bool(5), type 9 => Bool(6), type 10 => Bool(7)
        self.mode = [
            0
        ] * self.channels  # temp mode is an int here, 0:__,1:C,2:F (this is different than other places)
        self.div = [0] * self.channels

        self.optimizer = True  # if set, values of consecutive register addresses are requested in single requests
        self.fetch_max_blocks = False  # if set, the optimizer fetches only one sequence per area from the minimum to the maximum register ignoring gaps
        # S7 areas associated to dicts associating S7 DB numbers to start registers in use
        # for optimized read of full register segments with single requests
        # this dict is re-computed on each connect() by a call to updateActiveRegisters()
        # NOTE: for registers of type float (32bit = 2x16bit) also the succeeding register is registered here
        self.activeRegisters = {}
        # the readings cache that is filled by requesting sequences of values in blocks
        self.readingsCache = {}

        self.PID_area = 0
        self.PID_db_nr = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""
        self.PIDmultiplier = 0
        self.SVtype = 0
        self.SVmultiplier = 0

        self.COMsemaphore = QSemaphore(1)

        self.areas = [
            0x81,  # PE
            0x82,  # PA
            0x83,  # MK
            0x1C,  # CT
            0x1D,  # TM
            0x84,  # DB
        ]

        self.plc = None
        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data
        self.libLoaded = False

################
# conversion methods copied from s7:util.py

    def get_bool(self, _bytearray, byte_index, bool_index):
        """
        Get the boolean value from location in bytearray
        """
        index_value = 1 << bool_index
        byte_value = _bytearray[byte_index]
        current_value = byte_value & index_value
        return current_value == index_value

    def set_bool(self, _bytearray, byte_index, bool_index, value):
        """
        Set boolean value on location in bytearray
        """
        assert value in [0, 1, True, False]
        current_value = self.get_bool(_bytearray, byte_index, bool_index)
        index_value = 1 << bool_index

        # check if bool already has correct value
        if current_value == value:
            return

        if value:
            # make sure index_v is IN current byte
            _bytearray[byte_index] += index_value
        else:
            # make sure index_v is NOT in current byte
            _bytearray[byte_index] -= index_value

    def set_int(self, _bytearray, byte_index, _int):
        """
        Set value in bytearray to int
        """
        # make sure were dealing with an int
        _int = int(_int)
        _bytes = struct.unpack('2B', struct.pack('>h', _int))
        _bytearray[byte_index:byte_index + 2] = _bytes

    def get_int(self, _bytearray, byte_index):
        """
        Get int value from bytearray.
    
        int are represented in two bytes
        """
        data = _bytearray[byte_index:byte_index + 2]
        data[1] = data[
            1] & 0xFF  # added to fix a conversion problem: see https://github.com/gijzelaerr/python-snap7/issues/101
        value = struct.unpack('>h', struct.pack('2B', *data))[0]
        return value

    def set_real(self, _bytearray, byte_index, real):
        """
        Set Real value
    
        make 4 byte data from real
    
        """
        real = float(real)
        real = struct.pack('>f', real)
        _bytes = struct.unpack('4B', real)
        for i, b in enumerate(_bytes):
            _bytearray[byte_index + i] = b

    def get_real(self, _bytearray, byte_index):
        """
        Get real value. create float from 4 bytes
        """
        x = _bytearray[byte_index:byte_index + 4]
        real = struct.unpack('>f', struct.pack('4B', *x))[0]
        return real

################

    def setPID(self, p, i, d, PIDmultiplier):
        if self.PID_area and not (self.PID_p_register == self.PID_i_register ==
                                  self.PID_d_register == 0):
            multiplier = 1.
            if PIDmultiplier == 1:
                PIDmultiplier = 10.
            elif PIDmultiplier == 2:
                multiplier = 100.
            self.writeInt(self.PID_area - 1, self.PID_db_nr,
                          self.PID_p_register, p * multiplier)
            self.writeInt(self.PID_area - 1, self.PID_area, self.PID_db_nr,
                          self.PID_i_register, i * multiplier)
            self.writeInt(self.PID_area - 1, self.PID_area, self.PID_db_nr,
                          self.PID_d_register, d * multiplier)

    def setTarget(self, sv, SVmultiplier):
        if self.PID_area:
            multiplier = 1.
            if SVmultiplier == 1:
                multiplier = 10.
            elif SVmultiplier == 2:
                multiplier = 100.
            if self.SVtype == 1:
                self.writeFloat(self.PID_area - 1, self.PID_db_nr,
                                self.PID_SV_register, sv * multiplier)
            else:
                self.writeInt(self.PID_area - 1,
                              self.PID_db_nr, self.PID_SV_register,
                              int(round(sv * multiplier)))

    def isConnected(self):
        # the check on the CPU state is needed as get_connected() still returns True if the connect got terminated from the peer due to a bug in snap7
        # disconnects and clears the S7 plc objects if get_connected() but not str(self.plc.get_cpu_state()) == "S7CpuStatusRun" to force a clear restart
        #        return self.plc is not None and self.plc.get_connected() and str(self.plc.get_cpu_state()) == "S7CpuStatusRun"
        if self.plc is not None and self.plc.get_connected():
            return True
#            if str(self.plc.get_cpu_state()) == "S7CpuStatusRun":
#                return True
#            else:
#                self.disconnect()
#                return False
        else:
            return False

    def disconnect(self):
        try:
            self.plc.plc_stop()
        except:
            pass
        try:
            self.plc.disconnect()
        except:
            pass
        try:
            self.plc.destroy()
        except:
            pass
        self.plc = None

    def connect(self):
        if not self.libLoaded:
            #from artisanlib.s7client import S7Client
            from snap7.common import load_library as load_snap7_library
            # first load shared lib if needed
            platf = str(platform.system())
            if platf in ['Windows', 'Linux'] and artisanlib.util.appFrozen():
                libpath = os.path.dirname(sys.executable)
                if platf == 'Linux':
                    snap7dll = os.path.join(libpath, "libsnap7.so")
                else:  # Windows:
                    snap7dll = os.path.join(libpath, "snap7.dll")
                load_snap7_library(
                    snap7dll)  # will ensure to load it only once
            self.libLoaded = True

        if self.libLoaded and self.plc is None:
            # create a client instance
            from artisanlib.s7client import S7Client
            self.plc = S7Client()

        # next reset client instance if not yet connected to ensure a fresh start
        if not self.isConnected():
            try:
                if self.plc is None:
                    from artisanlib.s7client import S7Client
                    self.plc = S7Client()
                else:
                    self.plc.disconnect()
            except:
                pass
            with suppress_stdout_stderr():
                time.sleep(0.4)
                try:
                    self.plc.connect(self.host, self.rack, self.slot,
                                     self.port)
                    time.sleep(0.4)
                except:
                    pass

            if self.isConnected():
                self.aw.sendmessage(
                    QApplication.translate("Message", "S7 connected", None))
                self.clearReadingsCache()
                time.sleep(0.4)
            else:
                time.sleep(0.6)
                try:
                    if self.plc is None:
                        from artisanlib.s7client import S7Client
                        self.plc = S7Client()
                    else:
                        self.plc.disconnect()
                except:
                    pass
                # we try a second time
                with suppress_stdout_stderr():
                    time.sleep(0.4)
                    self.plc.connect(self.host, self.rack, self.slot,
                                     self.port)
                    time.sleep(0.4)

                    if self.isConnected():
                        self.clearReadingsCache()
                        self.aw.sendmessage(
                            QApplication.translate("Message", "S7 Connected",
                                                   None) + " (2)")
                        time.sleep(0.4)
            self.updateActiveRegisters()

########## S7 optimizer for fetching register data in batches

# S7 area => db_nr => [start registers]

    def updateActiveRegisters(self):
        self.activeRegisters = {}
        for c in range(self.channels):
            area = self.area[c] - 1
            if area != -1:
                db_nr = self.db_nr[c]
                register = self.start[c]
                registers = [register]  # BOOL
                if self.type[c] in [1, 2]:  # FLOAT (or FLOAT2INT)
                    registers.append(register + 1)
                    registers.append(register + 2)
                    registers.append(register + 3)
                elif self.type[c] == 0:  # INT
                    registers.append(register + 1)
                if not (area in self.activeRegisters):
                    self.activeRegisters[area] = {}
                if db_nr in self.activeRegisters[area]:
                    self.activeRegisters[area][db_nr].extend(registers)
                else:
                    self.activeRegisters[area][db_nr] = registers

    def clearReadingsCache(self):
        self.readingsCache = {}

    def cacheReadings(self, area, db_nr, register, results):
        if not (area in self.readingsCache):
            self.readingsCache[area] = {}
        if not db_nr in self.readingsCache[area]:
            self.readingsCache[area][db_nr] = {}
        try:
            for i, v in enumerate(results):
                self.readingsCache[area][db_nr][register + i] = v
        except:
            pass

    def readActiveRegisters(self):
        if not self.optimizer:
            return
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.clearReadingsCache()
            self.connect()
            if self.isConnected():
                for area in self.activeRegisters:
                    for db_nr in self.activeRegisters[area]:
                        registers = sorted(self.activeRegisters[area][db_nr])
                        if self.fetch_max_blocks:
                            sequences = [[registers[0], registers[-1]]]
                        else:
                            # split in successive sequences
                            gaps = [[s, e]
                                    for s, e in zip(registers, registers[1:])
                                    if s + 1 < e]
                            edges = iter(registers[:1] + sum(gaps, []) +
                                         registers[-1:])
                            sequences = list(
                                zip(edges, edges)
                            )  # list of pairs of the form (start-register,end-register)
                        just_send = False
                        for seq in sequences:
                            retry = self.readRetries
                            register = seq[0]
                            count = seq[1] - seq[0] + 1
                            res = None
                            while True:
                                try:
                                    if just_send:
                                        time.sleep(0.03)
                                    just_send = True
                                    res = self.plc.read_area(
                                        self.areas[area], db_nr, register,
                                        count)
                                except:
                                    res = None
                                if res is None:
                                    if retry > 0:
                                        retry = retry - 1
                                    else:
                                        raise Exception(
                                            "read_area({},{},{},{})".format(
                                                area, db_nr, register, count))
                                else:
                                    break
                            if res is not None:
                                if self.commError:  # we clear the previous error and send a message
                                    self.commError = False
                                    self.aw.qmc.adderror(
                                        QApplication.translate(
                                            "Error Message",
                                            "S7 Communication Resumed", None))
                                self.cacheReadings(area, db_nr, register, res)

                            #note: logged chars should be unicode not binary
                            if self.aw.seriallogflag:
                                self.aw.addserial(
                                    "S7 read_area({},{},{},{})".format(
                                        area, db_nr, register, count))
        except Exception as e:  # as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            _, _, exc_tb = sys.exc_info()
            #            self.aw.qmc.adderror((QApplication.translate("Error Message","S7 Error:",None) + " readSingleRegister() {0}").format(str(ex)),exc_tb.tb_lineno)
            _, _, exc_tb = sys.exc_info()
            self.aw.qmc.adderror(
                QApplication.translate(
                    "Error Message",
                    "readActiveRegisters() S7 Communication Error", None) +
                ": " + str(e), exc_tb.tb_lineno)
            if self.aw.seriallogflag:
                self.aw.addserial(
                    "S7 readActiveRegisters() => S7 Communication Error: {}".
                    format(str(e)))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

##########

    def writeFloat(self, area, dbnumber, start, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            4)
                    self.set_real(ba, 0, float(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)

            else:
                self.aw.qmc.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " writeFloat: " + str(e), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            if self.aw.seriallogflag:
                self.aw.addserial("S7 writeFloat({},{},{},{})".format(
                    area, dbnumber, start, value))

    def writeInt(self, area, dbnumber, start, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            2)
                    self.set_int(ba, 0, int(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)

            else:
                self.aw.qmc.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " writeInt: " + str(e), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            if self.aw.seriallogflag:
                self.aw.addserial("S7 writeInt({},{},{},{})".format(
                    area, dbnumber, start, value))

    def maskWriteInt(self, area, dbnumber, start, and_mask, or_mask, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            2)
                    new_val = (int(round(value))
                               & and_mask) | (or_mask & (and_mask ^ 0xFFFF))
                    self.set_int(ba, 0, int(new_val))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)
            else:
                self.aw.qmc.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " maskWriteInt: " + str(e), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            if self.aw.seriallogflag:
                self.aw.addserial("S7 writeInt({},{},{},{})".format(
                    area, dbnumber, start, value))

    def writeBool(self, area, dbnumber, start, index, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            1)
                    self.set_bool(ba, 0, int(index), bool(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)
            else:
                self.aw.qmc.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " writeBool: " + str(e), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            if self.aw.seriallogflag:
                self.aw.addserial("S7 writeBool({},{},{},{},{})".format(
                    area, dbnumber, start, index, value))

    # if force the readings cache is ignored and fresh readings are requested
    def readFloat(self, area, dbnumber, start, force=False):
        if area == 0:
            return
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            if not force and area in self.readingsCache and dbnumber in self.readingsCache[area] and start in self.readingsCache[area][dbnumber] \
                and start+1 in self.readingsCache[area][dbnumber] and start+2 in self.readingsCache[area][dbnumber] \
                and start+3 in self.readingsCache[area][dbnumber]:
                # cache hit
                res = bytearray([
                    self.readingsCache[area][dbnumber][start],
                    self.readingsCache[area][dbnumber][start + 1],
                    self.readingsCache[area][dbnumber][start + 2],
                    self.readingsCache[area][dbnumber][start + 3]
                ])
                r = self.get_real(res, 0)
                if self.aw.seriallogflag and not self.commError:
                    self.aw.addserial(
                        "S7 readFloat_cached({},{},{},{}) => {}".format(
                            area, dbnumber, start, force, r))
                return r
            else:
                self.connect()
                if self.isConnected():
                    retry = self.readRetries
                    res = None
                    while True:
                        try:
                            with suppress_stdout_stderr():
                                res = self.plc.read_area(
                                    self.areas[area], dbnumber, start, 4)

                        except:
                            res = None
                        if res is None:
                            if retry > 0:
                                retry = retry - 1
                            else:
                                raise Exception("result None")
                        else:
                            break
                    if res is None:
                        return
                    else:
                        if self.commError:  # we clear the previous error and send a message
                            self.commError = False
                            self.aw.qmc.adderror(
                                QApplication.translate(
                                    "Error Message",
                                    "S7 Communication Resumed", None))
                        r = self.get_real(res, 0)
                        if self.aw.seriallogflag and not self.commError:
                            self.aw.addserial(
                                "S7 readFloat({},{},{},{}) => {}".format(
                                    area, dbnumber, start, force, r))
                        return r
                else:
                    self.commError = True
                    self.aw.qmc.adderror((QApplication.translate(
                        "Error Message", "S7 Error:", None) +
                                          " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " readFloat({},{},{},{}): {}".format(
                        area, dbnumber, start, force, str(e)),
                    exc_tb.tb_lineno)
                if self.aw.seriallogflag:
                    self.aw.addserial(
                        "S7 readFloat({},{},{},{}) => S7 Communication Error: {}"
                        .format(area, dbnumber, start, force, str(e)))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # as readFloat, but does not retry nor raise and error and returns a None instead
    # also does not reserve the port via a semaphore nor uses the cache!
    def peakFloat(self, area, dbnumber, start):
        if area == 0:
            return
        try:
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    res = self.plc.read_area(self.areas[area], dbnumber, start,
                                             4)
                return self.get_real(res, 0)
            else:
                return
        except:
            return

    # if force the readings cache is ignored and fresh readings are requested
    def readInt(self, area, dbnumber, start, force=False):
        if area == 0:
            return
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            if not force and area in self.readingsCache and dbnumber in self.readingsCache[area] and start in self.readingsCache[area][dbnumber] \
                and start+1 in self.readingsCache[area][dbnumber]:
                # cache hit
                res = bytearray([
                    self.readingsCache[area][dbnumber][start],
                    self.readingsCache[area][dbnumber][start + 1]
                ])
                r = self.get_int(res, 0)
                if self.aw.seriallogflag:
                    self.aw.addserial(
                        "S7 readInt_cached({},{},{},{}) => {}".format(
                            area, dbnumber, start, force, r))
                return r
            else:
                self.connect()
                if self.isConnected():
                    retry = self.readRetries
                    res = None
                    while True:
                        try:
                            with suppress_stdout_stderr():
                                res = self.plc.read_area(
                                    self.areas[area], dbnumber, start, 2)
                        except Exception:
                            res = None
                        if dbnumber == 2 and start == 48:
                            raise Exception("result None")
                        if res is None:
                            if retry > 0:
                                retry = retry - 1
                            else:
                                raise Exception("result None")
                        else:
                            break
                    if res is None:
                        return
                    else:
                        if self.commError:  # we clear the previous error and send a message
                            self.commError = False
                            self.aw.qmc.adderror(
                                QApplication.translate(
                                    "Error Message",
                                    "S7 Communication Resumed", None))
                        r = self.get_int(res, 0)
                        if self.aw.seriallogflag and not self.commError:
                            self.aw.addserial(
                                "S7 readInt({},{},{},{}) => {}".format(
                                    area, dbnumber, start, force, r))
                        return r
                else:
                    self.commError = True
                    self.aw.qmc.adderror((QApplication.translate(
                        "Error Message", "S7 Error:", None) +
                                          " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " readInt({},{},{},{}): {}".format(area, dbnumber, start,
                                                       force, str(e)),
                    exc_tb.tb_lineno)
                if self.aw.seriallogflag:
                    self.aw.addserial(
                        "S7 readInt({},{},{},{}) => S7 Communication Error: {}"
                        .format(area, dbnumber, start, force, str(e)))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # as readInt, but does not retry nor raise and error and returns a None instead
    # also does not reserve the port via a semaphore nor uses the cache!
    def peekInt(self, area, dbnumber, start):
        if area == 0:
            return
        try:
            self.connect()
            if self.isConnected():
                with suppress_stdout_stderr():
                    res = self.plc.read_area(self.areas[area], dbnumber, start,
                                             2)
                return self.get_int(res, 0)
            else:
                return
        except:
            return

    # if force the readings cache is ignored and fresh readings are requested
    def readBool(self, area, dbnumber, start, index, force=False):
        if area == 0:
            return
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            if not force and area in self.readingsCache and dbnumber in self.readingsCache[
                    area] and start in self.readingsCache[area][dbnumber]:
                # cache hit
                res = bytearray([self.readingsCache[area][dbnumber][start]])
                r = self.get_bool(res, 0, index)
                if self.aw.seriallogflag:
                    self.aw.addserial(
                        "S7 readBool_cached({},{},{},{},{}) => {}".format(
                            area, dbnumber, start, index, force, r))
                return r
            else:
                self.connect()
                if self.isConnected():
                    retry = self.readRetries
                    res = None
                    while True:
                        try:
                            with suppress_stdout_stderr():
                                res = self.plc.read_area(
                                    self.areas[area], dbnumber, start, 1)

                        except Exception:
                            res = None
                        if res is None:
                            if retry > 0:
                                retry = retry - 1
                            else:
                                raise Exception("result None")
                        else:
                            break
                    if res is None:
                        return
                    else:
                        if self.commError:  # we clear the previous error and send a message
                            self.commError = False
                            self.aw.qmc.adderror(
                                QApplication.translate(
                                    "Error Message",
                                    "S7 Communication Resumed", None))
                        r = self.get_bool(res, 0, index)
                        if self.aw.seriallogflag and not self.commError:
                            self.aw.addserial(
                                "S7 readBool({},{},{},{},{}) => {}".format(
                                    area, dbnumber, start, index, force, r))
                        return r
                else:
                    self.commError = True
                    self.aw.qmc.adderror((QApplication.translate(
                        "Error Message", "S7 Error:", None) +
                                          " connecting to PLC failed"))
        except Exception as e:
            if self.aw.qmc.flagon:
                _, _, exc_tb = sys.exc_info()
                self.aw.qmc.adderror(
                    QApplication.translate("Error Message",
                                           "S7 Communication Error", None) +
                    " readBool({},{},{},{},{}): {}".format(
                        area, dbnumber, start, index, force, str(e)),
                    exc_tb.tb_lineno)
                if self.aw.seriallogflag:
                    self.aw.addserial(
                        "S7 readBool({},{},{},{},{}) => S7 Communication Error: {}"
                        .format(area, dbnumber, start, index, force, str(e)))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            if self.aw.seriallogflag:
                self.aw.addserial("S7 readBool({},{},{},{})".format(
                    area, dbnumber, start, index))
Exemple #22
0
import shelve
import portalocker
import requests

from PyQt5.QtCore import QSemaphore, QTimer
from PyQt5.QtWidgets import QApplication

from plus import config, util, connection, controller, roast

#### Sync Cache

# holding all roast UUIDs under sync with the server
# shared cache between the Artisan and the ArtisanViewer app

sync_cache_semaphore = QSemaphore(1)


# if lock is True, return the path of the corresponding lock file
def getSyncPath(lock=False):
    if config.account_nr is None or config.account_nr == 0:
        fn = config.sync_cache
    else:
        fn = config.sync_cache + str(config.account_nr)
    if lock:
        fn = fn + "_lock"
    return util.getDirectory(fn, share=True)


# register the modified_at timestamp (EPOC as float with milliseoncds) for the given uuid, assuming it holds the last timepoint modifications were last synced with the server
def addSync(uuid, modified_at):
Exemple #23
0
class s7port(object):
    def __init__(self, sendmessage, adderror, addserial):
        self.sendmessage = sendmessage  # function to create an Artisan message to the user in the message line
        self.adderror = adderror  # signal an error to the user
        self.addserial = addserial  # add to serial log

        self.readRetries = 1
        self.channels = 8  # maximal number of S7 channels
        self.host = '127.0.0.1'  # the TCP host
        self.port = 102  # the TCP port
        self.rack = 0  # 0,..,7
        self.slot = 0  # 0,..,31

        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider S/ actions with "read" command

        self.area = [0] * self.channels
        self.db_nr = [1] * self.channels
        self.start = [0] * self.channels
        self.type = [0] * self.channels
        self.mode = [
            0
        ] * self.channels  # temp mode is an int here, 0:__,1:C,2:F (this is different than other places)
        self.div = [0] * self.channels

        self.PID_area = 0
        self.PID_db_nr = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""
        self.SVmultiplier = 0
        self.PIDmultiplier = 0

        self.COMsemaphore = QSemaphore(1)

        self.areas = [
            0x81,  # PE
            0x82,  # PA
            0x83,  # MK
            0x1C,  # CT
            0x1D,  # TM
            0x84,  # DB
        ]

        self.plc = None
        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data

    def setPID(self, p, i, d, PIDmultiplier):
        if self.PID_area and not (self.PID_p_register == self.PID_i_register ==
                                  self.PID_d_register == 0):
            multiplier = 1.
            if PIDmultiplier == 1:
                PIDmultiplier = 10.
            elif PIDmultiplier == 2:
                multiplier = 100.
            self.writeInt(self.PID_area - 1, self.PID_db_nr,
                          self.PID_p_register, p * multiplier)
            self.writeInt(self.PID_area - 1, self.PID_area, self.PID_db_nr,
                          self.PID_i_register, i * multiplier)
            self.writeInt(self.PID_area - 1, self.PID_area, self.PID_db_nr,
                          self.PID_d_register, d * multiplier)

    def setTarget(self, sv, SVmultiplier):
        if self.PID_area:
            multiplier = 1.
            if SVmultiplier == 1:
                multiplier = 10.
            elif SVmultiplier == 2:
                multiplier = 100.
            self.writeInt(self.PID_area - 1, self.PID_db_nr,
                          self.PID_SV_register, int(round(sv * multiplier)))

    def isConnected(self):
        return not (self.plc is None) and self.plc.get_connected()

    def disconnect(self):
        if self.isConnected():
            try:
                self.plc.disconnect()
                self.plc.destroy()
                self.plc = None
            except Exception:
                pass

    def connect(self):
        from artisanlib.s7client import S7Client
        from snap7.common import load_library as load_snap7_library
        # first load shared lib if needed
        platf = str(platform.system())
        if platf in ['Windows', 'Linux'] and util.appFrozen():
            libpath = os.path.dirname(sys.executable)
            if platf == 'Linux':
                snap7dll = os.path.join(libpath, "libsnap7.so")
            else:  # Windows:
                snap7dll = os.path.join(libpath, "snap7.dll")
            load_snap7_library(snap7dll)  # will ensure to load it only once
        # next reset client instance if not yet connected to ensure a fresh start
        if self.plc and not self.plc.get_connected():
            self.plc = None
        # connect if not yet connected
        if self.plc is None:
            self.plc = S7Client()
            with suppress_stdout_stderr():
                time.sleep(0.3)
                self.plc.connect(self.host, self.rack, self.slot, self.port)
            if self.plc.get_connected():
                self.sendmessage(
                    QApplication.translate("Message", "S7 Connected", None))
                time.sleep(0.7)
            else:
                time.sleep(0.5)
                self.plc = S7Client()
                # we try a second time
                with suppress_stdout_stderr():
                    time.sleep(0.3)
                    self.plc.connect(self.host, self.rack, self.slot,
                                     self.port)
                if self.plc.get_connected():
                    self.sendmessage(
                        QApplication.translate("Message", "S7 Connected",
                                               None))
                    time.sleep(0.7)

    def writeFloat(self, area, dbnumber, start, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            4)
                    from snap7.util import set_real
                    set_real(ba, 0, float(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)
            else:
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception as ex:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    def writeInt(self, area, dbnumber, start, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                with suppress_stdout_stderr():
                    ba = self.plc.read_area(self.areas[area], dbnumber, start,
                                            2)
                    from snap7.util import set_int
                    set_int(ba, 0, int(value))
                    self.plc.write_area(self.areas[area], dbnumber, start, ba)
            else:
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
        except Exception:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    def readFloat(self, area, dbnumber, start):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                retry = self.readRetries
                res = None
                while True:
                    try:
                        with suppress_stdout_stderr():
                            res = self.plc.read_area(self.areas[area],
                                                     dbnumber, start, 4)
                    except:
                        res = None
                    if res is None:
                        if retry > 0:
                            retry = retry - 1
                        else:
                            raise Exception("Communication error")
                    else:
                        break
                if res is None:
                    return -1
                else:
                    if self.commError:  # we clear the previous error and send a message
                        self.commError = False
                        self.adderror(
                            QApplication.translate("Error Message",
                                                   "S7 Communication Resumed",
                                                   None))
                    from snap7.util import get_real
                    return get_real(res, 0)
            else:
                self.commError = True
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
                return -1
        except Exception:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None))
            self.commError = True
            return -1
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            self.addserial("S7 readFloat")

    def readInt(self, area, dbnumber, start):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if self.plc is not None and self.plc.get_connected():
                retry = self.readRetries
                res = None
                while True:
                    try:
                        with suppress_stdout_stderr():
                            res = self.plc.read_area(self.areas[area],
                                                     dbnumber, start, 2)
                    except Exception as e:
                        res = None
                    if res is None:
                        if retry > 0:
                            retry = retry - 1
                        else:
                            raise Exception("Communication error")
                    else:
                        break
                if res is None:
                    return -1
                else:
                    if self.commError:  # we clear the previous error and send a message
                        self.commError = False
                        self.adderror(
                            QApplication.translate("Error Message",
                                                   "S7 Communication Resumed",
                                                   None))
                    from snap7.util import get_int
                    return get_int(res, 0)
            else:
                self.commError = True
                self.adderror(
                    (QApplication.translate("Error Message", "S7 Error:", None)
                     + " connecting to PLC failed"))
                return -1
        except Exception:
            self.adderror(
                QApplication.translate("Error Message",
                                       "S7 Communication Error", None))
            self.commError = True
            return -1
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            self.addserial("S7 readInt")
Exemple #24
0
class ApplicationWindow(QtWidgets.QMainWindow):
    """
    Main window of the application
    """
    stopSignal = pyqtSignal()

    def __init__(self):
        QtWidgets.QMainWindow.__init__(self)

        selfdir = os.path.dirname(__file__)
        uic.loadUi(os.path.join(selfdir, "mainwindow.ui"), self)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        self.app = QtWidgets.QApplication.instance()
        self.state = "INIT"

        self.eegSettings = {}

        self.__initEEGInputs()
        self.__initBrowseButton()
        self.__initSetWindowSizeButton()
        self.__initRunInputs()
        self.__initRunButtons()
        self.__initNewPlotAction()
        self.__initOptionsAction()

        self.rtDelay = self.simDelay = 1 / 8

        self.functions = []
        self.windowList = []

    def __setState(self, state):
        def enabledElements(dsB, esB, rB, runEl, play):
            self.dataSourceBox.setEnabled(dsB)
            self.actionBrowse.setEnabled(dsB)

            self.windowSizeBox.setEnabled(esB)

            self.runBox.setEnabled(rB)

            self.startInput.setEnabled(runEl)
            self.stopInput.setEnabled(runEl)
            self.actionOptions.setEnabled(runEl)
            self.actionNewPlot.setEnabled(runEl)
            self.newPlotButton.setEnabled(runEl)

            self.playButton.setEnabled(play)
            self.pauseButton.setEnabled(not play)

        if state == "STOP":
            enabledElements(True, True, True, True, True)
        elif state == "PLAY":
            enabledElements(False, False, True, False, False)
        elif state == "PAUSE":
            enabledElements(False, False, True, False, True)

        self.state = state

    def __initOptionsAction(self):
        def openOptionsDialog():
            sampleRate = self.helper.sampleRate

            samples = int(np.round(self.simDelay * sampleRate))
            speedMul = self.simDelay / self.rtDelay

            od = OptionsDialog(parent=self, samples=samples, speedMul=speedMul)
            od.show()

        self.actionOptions.triggered.connect(openOptionsDialog)

    def __initNewPlotAction(self):
        def newPlotWindow():
            pw = PlotWindow(self)
            self.windowList.append(pw)
            self.functions.append(pw.update)
            pw.show()

        self.prevSaveDir = ""
        self.actionNewPlot.triggered.connect(newPlotWindow)
        self.newPlotButton.clicked.connect(newPlotWindow)

    def __initBrowseButton(self):
        def openFileDialog():
            fileFilter = "CSV-Files (*.csv);; EDF-Files (*.edf)"
            filename = QtWidgets.QFileDialog.getOpenFileName(
                self, directory=self.prevBrowseDir, filter=fileFilter)

            if filename[0] != "":
                try:
                    #Settings preparation
                    ica = self.icaCB.isChecked()
                    normalize = self.normalizeCB.isChecked()

                    #Helper creation
                    ext = os.path.splitext(filename[0])[1]
                    if ext == ".edf":
                        self.helper = EDFHelper(filename[0],
                                                ICA=ica,
                                                normalize=normalize)
                    else:
                        sampleRate, state = QtWidgets.QInputDialog.getInt(
                            self,
                            "Sample Rate",
                            "Sample Rate",
                            value=128,
                            min=0)

                        self.helper = CSVHelper(filename[0],
                                                sampleRate=sampleRate,
                                                ICA=ica,
                                                normalize=normalize)

                    # Letting the user select the channels
                    nChannels = self.helper.nChannels
                    names = self.helper.names
                    dialog = ChannelSelectorDialog(nChannels, names, self)
                    if (dialog.exec()):
                        self.helper.selectSignals(dialog.getChannel())

                    del dialog

                    # Next time button clicked the dialog will be opened in
                    # prevBrowseDir
                    self.prevBrowseDir = filename[0]

                    #Storing windowSize and sampleRate
                    windowSize = self.helper.eeg.windowSize
                    self.eegSettings["windowSize"] = windowSize
                    sampleRate = self.helper.sampleRate
                    self.eegSettings["sampleRate"] = self.helper.sampleRate

                    #Giving feedback to user
                    self.feedBackLabel.setText("File oppened properly")
                    self.stopInput.setText(str(len(self.helper) / sampleRate))
                    self.windowSizeInput.setText(str(windowSize))

                    #Unlocking locked inputs
                    self.__setState("STOP")

                    #Reset plots if there where another file previously
                    self._resetPlots()

                except IOError:
                    QtWidgets.QMessageBox.warning(self, "Error",
                                                  "Error opening the file",
                                                  QtWidgets.QMessageBox.Ok)

                except ValueError:
                    QtWidgets.QMessageBox.warning(
                        self, "Error",
                        "Error reading the file." + "Incorrect format?",
                        QtWidgets.QMessageBox.Ok)

                except Exception as e:
                    QtWidgets.QMessageBox.warning(
                        self, "Error", "Unexpected Error\n" + str(e),
                        QtWidgets.QMessageBox.Ok)

        self.prevBrowseDir = ""
        self.browseButton.clicked.connect(openFileDialog)
        self.actionBrowse.triggered.connect(openFileDialog)

    def __initEEGInputs(self):
        def checkText(inputLine, text):
            if text == "":
                enabled = False
                styleSheet = "background: rgb(210, 30, 100, 160)"
            else:
                enabled = True
                styleSheet = ""

            inputLine.setStyleSheet(styleSheet)
            self.setWindowSizeButton.setEnabled(enabled)

        wsInput = self.windowSizeInput
        wsInput.setValidator(QtGui.QIntValidator())
        wsInput.textChanged.connect(lambda text: checkText(wsInput, text))

    def __initSetWindowSizeButton(self):
        def setWindowSize():
            windowSize = int(self.windowSizeInput.text())
            self.eegSettings["windowSize"] = windowSize
            self.feedBackLabel.setText("New settings have been setted")
            self.__setState("STOP")
            self.helper.prepareEEG(windowSize)
            self._resetPlots()

        self.setWindowSizeButton.clicked.connect(setWindowSize)

    def __initRunInputs(self):
        self.startInput.setValidator(QtGui.QDoubleValidator())
        self.stopInput.setValidator(QtGui.QDoubleValidator())

    def _pause(self):
        self.semaphore.release(1)
        self.stopSignal.emit()
        self.loopThread.quit()
        self.loopThread.wait()

        self.__setState("PAUSE")

    def _stop(self):
        self._pause()

        self._resetPlots()

        self.__setState("STOP")

    def _play(self):
        #Input values
        start = float(self.startInput.text())
        stop = float(self.stopInput.text())

        #If state is PAUSE skip these steps
        if self.state != "PAUSE":
            self.timePosition = start

            #Iterator preparation
            sampleRate = self.eegSettings["sampleRate"]
            iterStep = int(round(sampleRate * self.simDelay))
            iterStart = int(start * sampleRate)
            iterStop = int(stop * sampleRate)
            #simDelay correction for int aproximation
            self.simDelay = iterStep / sampleRate

            self.iterator = iter(self.helper[iterStart:iterStop:iterStep])

            #Next iteration to test if values are correct
            try:
                next(self.iterator)
            except StopIteration:
                QtWidgets.QMessageBox.warning(
                    self, "Error", "The start and stop points are too close",
                    QtWidgets.QMessageBox.Ok)
                return
            #Initialize animations of windows
            for window in self.windowList:
                window.initAnimation(start)

        #Init semaphore and loopTrigger
        self.semaphore = QSemaphore(0)
        loop = LoopTrigger(self.semaphore, minDelay=self.rtDelay)
        self.loop = loop

        #Connect signals
        loop.sigUpdate.connect(self.__updateFields)
        loop.sigUpdate.connect(self.__playAnimation)

        self.stopSignal.connect(loop.stop)

        #Init thread
        self.loopThread = QThread()
        loop.moveToThread(self.loopThread)
        self.loopThread.started.connect(loop.loop)
        self.loopThread.start()

        #Set new state
        self.__setState("PLAY")

    def __initRunButtons(self):
        self.playButton.clicked.connect(self._play)
        self.stopButton.clicked.connect(self._stop)
        self.pauseButton.clicked.connect(self._pause)

    @pyqtSlot()
    def __playAnimation(self):
        try:
            next(self.iterator)
            for function in self.functions:
                try:
                    function()
                except:
                    self.functions.remove(function)

            self.app.processEvents()
            self.semaphore.release(1)

        except Exception as e:
            print(e)
            self._pause()

    @pyqtSlot()
    def __updateFields(self):
        self.timePosition += self.simDelay
        self.startInput.setText("%.2f" % self.timePosition)

    def _resetPlots(self):
        for win in self.windowList:
            win.reset()

    def deleteWinFromList(self, win):
        self.windowList.remove(win)
Exemple #25
0
import platform

if platform.system().startswith("Windows") or platform.system() == 'Darwin':
    import keyring.backends.fail  # @UnusedImport
    import keyring.backends.OS_X  # @UnusedImport
    import keyring.backends.SecretService  # @UnusedImport
    import keyring.backends.Windows  # @UnusedImport
import keyring  # @Reimport # imported last to make py2app work

from plus import config, account, util
from artisanlib import __version__

from PyQt5.QtCore import QSemaphore

token_semaphore = QSemaphore(
    1)  # protects access to the session token which is manipluated only here


def getToken():
    try:
        token_semaphore.acquire(1)
        return config.token
    except:
        return None
    finally:
        if token_semaphore.available() < 1:
            token_semaphore.release(1)


def getNickname():
    try:
Exemple #26
0
class PhidgetManager():

    def __init__(self):
        # a dictionary associating all physical attached Phidget channels
        # to their availablility state:
        #    True: available for attach to a software channel
        #    False: occupied and connected to a software channel
        # access to this dict is protected by the managersemaphore and
        # should happen only via the methods addChannel and deleteChannel
        self.attachedPhidgetChannels = {}
        self.managersemaphore = QSemaphore(1)
        self.manager = Manager()
        self.manager.setOnAttachHandler(self.attachHandler)
        self.manager.setOnDetachHandler(self.detachHandler)
        self.manager.open()
        
    def close(self):
        self.manager.close()
        self.attachedPhidgetChannels.clear()
        
    def attachHandler(self,_,attachedChannel):
        try:
            if attachedChannel.getParent().getDeviceClass() != DeviceClass.PHIDCLASS_HUB:
                # we do not add the hub itself
                self.addChannel(attachedChannel)
        except:
            pass
    
    def detachHandler(self,_,attachedChannel):
        try:
            self.deleteChannel(attachedChannel)
        except:
            pass
    
    def addChannel(self,channel):
        try:
            self.managersemaphore.acquire(1)
            state = True
            try:
                # reserve all channels with the same hubport on the same hub
                hub = channel.getHub()
                hubport = channel.getHubPort()
                hupportdevice = bool(channel.getIsHubPortDevice() == 0) # it is not a direct hubport channel
                for k, _ in self.attachedPhidgetChannels.items():
                    try:
                        khub = k.getHub()
                        khubport = k.getHubPort()
                        if khub == hub and khubport == hubport:
                            if hupportdevice:
                                if k.getIsHubPortDevice() != 0:
                                    self.attachedPhidgetChannels[k] = False
                                #else:
                                #  other is also a VINT device. Do nothing
                            else:
                                if k.getIsHubPortDevice() == 0:
                                    # there is a port registered with connected VINT device we deactivate this hubport channel
                                    state = False
                                #else:
                                #   do nothing
                    except:
                        pass
            except:
                pass
            self.attachedPhidgetChannels[channel] = state
        except Exception:
            pass
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)

    def deleteChannel(self,channel):
        try:
            self.managersemaphore.acquire(1)
            # if channel is a VINT device, release all HUBport channels that were blocked by this VINT device
            try:
                hub = channel.getHub()
                hubport = channel.getHubPort()
                hupportdevice = bool(channel.getIsHubPortDevice() == 0) # it is not a direct hubport channel
                if hupportdevice:
                    for k, _ in self.attachedPhidgetChannels.items():
                        if k != channel:
                            try:
                                khub = k.getHub()
                                khubport = k.getHubPort()
                                if khub == hub and khubport == hubport:                                    
                                    self.attachedPhidgetChannels[k] = True
                            except:
                                pass
            except:
                pass
            self.attachedPhidgetChannels.pop(channel, None)
        except Exception:
            pass
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)
                
    def getChannel(self,serial,port,channel,phidget_class_name,device_id,remote,remoteOnly):
        try:
            self.managersemaphore.acquire(1)
            if device_id in [DeviceID.PHIDID_HUB0000]:
                # we are looking for HUB ports
                hub = 1
            else:
                hub = 0
            for k, _ in self.attachedPhidgetChannels.items():
                if k.getIsHubPortDevice() or k.getDeviceClass() == DeviceClass.PHIDCLASS_VINT:
                    kport = k.getHubPort()
                else:
                    kport = None
                if k.getDeviceSerialNumber() == serial and (port is None or kport == port) and \
                    (hub or (k.getDeviceID() == device_id)) and \
                    ((remote and not remoteOnly) or (not remote and k.getIsLocal()) or (remote and remoteOnly and not k.getIsLocal())) and \
                    k.getChannelClassName() == phidget_class_name and \
                    k.getChannel() == channel:
                    return k
            return None
        except Exception:
            return None
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)
                
    def reserveSerialPort(self,serial,port,channel,phidget_class_name,device_id,remote=False,remoteOnly=False):
        chnl = self.getChannel(serial,port,channel,phidget_class_name,device_id,remote,remoteOnly)
        self.reserveChannel(chnl)
        
    def releaseSerialPort(self,serial,port,channel,phidget_class_name,device_id,remote=False,remoteOnly=False):
        chnl = self.getChannel(serial,port,channel,phidget_class_name,device_id,remote,remoteOnly)
        self.releaseChannel(chnl)

    # should be called from the attach handler that binds this hardware channel to a software channel
    def reserveChannel(self,channel):
        try:
            self.managersemaphore.acquire(1)
            if channel is not None and channel in self.attachedPhidgetChannels:
                self.attachedPhidgetChannels[channel] = False
                if channel.getIsHubPortDevice():
                    hub = channel.getHub()
                    port = channel.getHubPort()
                    # reserve also all other channels with that hub/port combination
                    for k, _ in self.attachedPhidgetChannels.items():
                        try:
                            if k.getHub() == hub and k.getHubPort() == port:
                                self.attachedPhidgetChannels[k] = False
                        except:
                            pass
                #else:
                #  not a HUB port
        except Exception:
            pass
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)

    # should be called from the detach handler that releases this hardware channel fron a software channel
    def releaseChannel(self,channel):
        try:
            self.managersemaphore.acquire(1)
            if channel is not None and channel in self.attachedPhidgetChannels:
                self.attachedPhidgetChannels[channel] = True
                if channel.getIsHubPortDevice():
                    hub = channel.getHub()
                    port = channel.getHubPort()
                    # enable also all other channels with that hub/port combination
                    for k, _ in self.attachedPhidgetChannels.items():
                        try:
                            if k.getHub() == hub and k.getHubPort() == port:
                                self.attachedPhidgetChannels[k] = True
                        except:
                            pass                
        except Exception:
            pass
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)
                
#    def print_list(self,items):
#        for k,v in items:
#            print(v,k.getDeviceSerialNumber(),k.getDeviceClass(),k.getDeviceClassName(),k.getDeviceName(),k.getDeviceSKU(),k.getChannelClassName(),k.getDeviceID(),k.getIsHubPortDevice(),"port: ",k.getHubPort(),"ch: ", k.getChannel(), "local: ", k.getIsLocal())
#
#    def print_list2(self,items):
#        for k in items:
#            print(k.getDeviceSerialNumber(),k.getChannelClassName(),k.getDeviceID(),k.getIsHubPortDevice(),"port: ", k.getHubPort(),"ch: ",k.getChannel(), "local: ", k.getIsLocal())

    # returns the first matching Phidget channel and reserves it
    def getFirstMatchingPhidget(self,phidget_class_name,device_id,channel=None,remote=False,remoteOnly=False):
        try:
            self.managersemaphore.acquire(1)
            if device_id in [
                    DeviceID.PHIDID_HUB0000,
                    DeviceID.PHIDID_DIGITALINPUT_PORT,
                    DeviceID.PHIDID_DIGITALOUTPUT_PORT,
                    DeviceID.PHIDID_VOLTAGEINPUT_PORT,
                    DeviceID.PHIDID_VOLTAGERATIOINPUT_PORT]:
                # we are looking for HUB ports
                hub = 1
            else:
                hub = 0

#            self.print_list(self.attachedPhidgetChannels.items())

            # get list of all matching phidget channels
            matching_channels = [k for k, v in self.attachedPhidgetChannels.items() if v and \
                (hub or (k.getDeviceID() == device_id)) and \
                ((remote and not remoteOnly) or (not remote and k.getIsLocal()) or (remote and remoteOnly and not k.getIsLocal())) and \
                k.getChannelClassName() == phidget_class_name and \
                (channel is None or (not hub and channel == k.getChannel()) or (hub and k.getIsHubPortDevice() and k.getHubPort() == channel))]

#            self.print_list2(matching_channels)

            # sort by serial number (local first)
            matching_channels.sort(key=lambda x:x.getDeviceSerialNumber())
            # return smallest / first item
            if len(matching_channels) > 0:
                p = matching_channels[0]
                if p.getIsHubPortDevice() or p.getDeviceClass() == DeviceClass.PHIDCLASS_VINT:
                    port = p.getHubPort()
                else:
                    port = None
                return p.getDeviceSerialNumber(), port
            else:
                return None, None
        except Exception:
            return None, None
        finally:
            if self.managersemaphore.available() < 1:
                self.managersemaphore.release(1)
Exemple #27
0
# provided for educational purposes and is distributed in the hope that
# it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
# the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import shelve
from pathlib import Path

from PyQt5.QtCore import QSemaphore

from plus import config, util

register_semaphore = QSemaphore(1)

uuid_cache_path = util.getDirectory(config.uuid_cache)



# register the path for the given uuid, assuming it points to the .alog profile containing that uuid
def addPath(uuid,path):
    try:
        config.logger.debug("register:setPath(" + str(uuid) + "," + str(path) + ")")
        register_semaphore.acquire(1)
        with shelve.open(uuid_cache_path) as db:
            db[uuid] = path
    except Exception as e:
        config.logger.error("roast: Exception in addPath() %s",e)
    finally:
Exemple #28
0
class modbusport(object):
    """ this class handles the communications with all the modbus devices"""
    def __init__(self, sendmessage, adderror, addserial, aw):
        self.sendmessage = sendmessage  # function to create an Artisan message to the user in the message line
        self.adderror = adderror  # signal an error to the user
        self.addserial = addserial  # add to serial log
        self.aw = aw

        # retries
        self.readRetries = 1
        #default initial settings. They are changed by settingsload() at initiation of program acording to the device chosen
        self.comport = "COM5"  #NOTE: this string should not be translated.
        self.baudrate = 115200
        self.bytesize = 8
        self.parity = 'N'
        self.stopbits = 1
        self.timeout = 1.0
        self.PID_slave_ID = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""

        self.channels = 8
        self.inputSlaves = [0] * self.channels
        self.inputRegisters = [0] * self.channels
        self.inputFloats = [False] * self.channels
        self.inputBCDs = [False] * self.channels
        self.inputCodes = [3] * self.channels
        self.inputDivs = [0] * self.channels  # 0: none, 1: 1/10, 2:1/100
        self.inputModes = ["C"] * self.channels

        self.optimizer = True  # if set, values of consecutive register addresses are requested in single requests
        # MODBUS functions associated to dicts associating MODBUS slave ids to registers in use
        # for optimized read of full register segments with single requests
        # this dict is re-computed on each connect() by a call to updateActiveRegisters()
        # NOTE: for registers of type float and BCD (32bit = 2x16bit) also the succeeding register is registered here
        self.activeRegisters = {}
        # the readings cache that is filled by requesting sequences of values in blocks
        self.readingsCache = {}

        self.SVmultiplier = 0
        self.PIDmultiplier = 0
        self.byteorderLittle = False
        self.wordorderLittle = True
        self.master = None
        self.COMsemaphore = QSemaphore(1)
        self.host = '127.0.0.1'  # the TCP/UDP host
        self.port = 502  # the TCP/UDP port
        self.type = 0
        # type =
        #    0: Serial RTU
        #    1: Serial ASCII
        #    2: Serial Binary
        #    3: TCP
        #    4: UDP
        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider Modbus actions with "read" command

        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data

    # this garantees a minimum of 30 miliseconds between readings and 80ms between writes (according to the Modbus spec) on serial connections
    # this sleep delays between requests seems to be beneficial on slow RTU serial connections like those of the FZ-94
    def sleepBetween(self, write=False):
        if write:
            #            if self.type in [3,4]: # TCP or UDP
            #                time.sleep(0.040)
            pass  # handled in MODBUS lib
            #            else:
            time.sleep(0.035)
        else:
            if self.type in [
                    3, 4
            ]:  # delay between writes only on serial connections
                pass
            else:
                time.sleep(0.035)

    def address2register(self, addr, code=3):
        if code == 3 or code == 6:
            return addr - 40001
        else:
            return addr - 30001

    def isConnected(self):
        return not (self.master is None) and self.master.socket

    def disconnect(self):
        try:
            self.master.close()
        except Exception:
            pass
        self.master = None

    def connect(self):
        #        if self.master and not self.master.socket:
        #            self.master = None
        if self.master is None:
            self.commError = False
            try:
                # as in the following the port is None, no port is opened on creation of the (py)serial object
                if self.type == 1:  # Serial ASCII
                    from pymodbus.client.sync import ModbusSerialClient
                    self.master = ModbusSerialClient(method='ascii',
                                                     port=self.comport,
                                                     baudrate=self.baudrate,
                                                     bytesize=self.bytesize,
                                                     parity=self.parity,
                                                     stopbits=self.stopbits,
                                                     retry_on_empty=True,
                                                     timeout=self.timeout)
                elif self.type == 2:  # Serial Binary
                    from pymodbus.client.sync import ModbusSerialClient  # @Reimport
                    self.master = ModbusSerialClient(method='binary',
                                                     port=self.comport,
                                                     baudrate=self.baudrate,
                                                     bytesize=self.bytesize,
                                                     parity=self.parity,
                                                     stopbits=self.stopbits,
                                                     retry_on_empty=True,
                                                     timeout=self.timeout)
                elif self.type == 3:  # TCP
                    from pymodbus.client.sync import ModbusTcpClient
                    try:
                        self.master = ModbusTcpClient(
                            host=self.host,
                            port=self.port,
                            retry_on_empty=True,
                            retries=1,
                            timeout=0.9,  #self.timeout
                        )
                        self.readRetries = 0
                    except:
                        self.master = ModbusTcpClient(
                            host=self.host,
                            port=self.port,
                        )
                elif self.type == 4:  # UDP
                    from pymodbus.client.sync import ModbusUdpClient
                    try:
                        self.master = ModbusUdpClient(
                            host=self.host,
                            port=self.port,
                            retry_on_empty=True,
                            retries=3,
                            timeout=0.7,  #self.timeout
                        )
                    except:  # older versions of pymodbus don't support the retries, timeout nor the retry_on_empty arguments
                        self.master = ModbusUdpClient(
                            host=self.host,
                            port=self.port,
                        )
                else:  # Serial RTU
                    from pymodbus.client.sync import ModbusSerialClient  # @Reimport
                    self.master = ModbusSerialClient(
                        method='rtu',
                        port=self.comport,
                        baudrate=self.baudrate,
                        bytesize=self.bytesize,
                        parity=self.parity,
                        stopbits=self.stopbits,
                        retry_on_empty=False,
                        strict=
                        False,  # settings this to False disables the inter char timeout restriction
                        timeout=self.timeout)
                    #                    self.master.inter_char_timeout = 0.05
                    self.readRetries = 1
                self.master.connect()
                self.updateActiveRegisters()
                time.sleep(.5)  # avoid possible hickups on startup
                if self.isConnected() != None:
                    self.sendmessage(
                        QApplication.translate("Message",
                                               "Connected via MODBUS", None))
            except Exception as ex:
                _, _, exc_tb = sys.exc_info()
                self.adderror(
                    (QApplication.translate("Error Message", "Modbus Error:",
                                            None) + " connect() {0}").format(
                                                str(ex)), exc_tb.tb_lineno)

########## MODBUS optimizer for fetching register data in batches

# MODBUS code => slave => [registers]

    def updateActiveRegisters(self):
        self.activeRegisters = {}
        for c in range(self.channels):
            slave = self.inputSlaves[c]
            if slave != 0:
                register = self.inputRegisters[c]
                code = self.inputCodes[c]
                registers = [register]
                if self.inputFloats[c] or self.inputBCDs[c]:
                    registers.append(register + 1)
                if not (code in self.activeRegisters):
                    self.activeRegisters[code] = {}
                if slave in self.activeRegisters[code]:
                    self.activeRegisters[code][slave].extend(registers)
                else:
                    self.activeRegisters[code][slave] = registers

    def clearReadingsCache(self):
        self.readingsCache = {}

    def cacheReadings(self, code, slave, register, results):
        if not (code in self.readingsCache):
            self.readingsCache[code] = {}
        if not slave in self.readingsCache[code]:
            self.readingsCache[code][slave] = {}
        for i, v in enumerate(results):
            self.readingsCache[code][slave][register + i] = v

    def readActiveRegisters(self):
        if not self.optimizer:
            return
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.clearReadingsCache()
            for code in self.activeRegisters:
                for slave in self.activeRegisters[code]:
                    registers = sorted(self.activeRegisters[code][slave])
                    # split in successive sequences
                    gaps = [[s, e] for s, e in zip(registers, registers[1:])
                            if s + 1 < e]
                    edges = iter(registers[:1] + sum(gaps, []) +
                                 registers[-1:])
                    sequences = list(
                        zip(edges, edges)
                    )  # list of pairs of the form (start-register,end-register)
                    just_send = False
                    for seq in sequences:
                        retry = self.readRetries
                        register = seq[0]
                        count = seq[1] - seq[0] + 1
                        res = None
                        if just_send:
                            self.sleepBetween(
                            )  # we start with a sleep, as it could be that just a send command happend before the semaphore was catched
                        just_send = True
                        while True:
                            try:
                                # we cache only MODBUS function 3 and 4 (not 1 and 2!)
                                if code == 3:
                                    res = self.master.read_holding_registers(
                                        register, count, unit=slave)
                                elif code == 4:
                                    res = self.master.read_input_registers(
                                        register, count, unit=slave)
                            except Exception:
                                res = None
                            if res is None or res.isError(
                            ):  # requires pymodbus v1.5.1
                                if retry > 0:
                                    retry = retry - 1
                                    time.sleep(0.020)
                                else:
                                    raise Exception("Exception response")
                            else:
                                break
                        if res is not None:
                            if self.commError:  # we clear the previous error and send a message
                                self.commError = False
                                self.adderror(
                                    QApplication.translate(
                                        "Error Message",
                                        "Modbus Communication Resumed", None))
                            self.cacheReadings(code, slave, register,
                                               res.registers)

                        #note: logged chars should be unicode not binary
                        if self.aw.seriallogflag:
                            ser_str = "MODBUS readSingleRegister : {},{},{},{},{},{} || Slave = {} || Register = {} || Code = {} || Rx = {}".format(
                                self.comport, self.baudrate, self.bytesize,
                                self.parity, self.stopbits, self.timeout,
                                slave, register, code, res)
                            self.addserial(ser_str)

        except Exception:  # as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readSingleRegister() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

##########

# function 15 (Write Multiple Coils)

    def writeCoils(self, slave, register, values):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_coils(int(register),
                                    list(values),
                                    unit=int(slave))
            time.sleep(.3)  # avoid possible hickups on startup
        except Exception as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeCoils() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 5 (Write Single Coil)
    def writeCoil(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_coil(int(register), value, unit=int(slave))
            time.sleep(.3)  # avoid possible hickups on startup
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeCoil() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # write value to register on slave (function 6 for int or function 16 for float)
    # value can be one of string (containing an int or float), an int or a float
    def writeRegister(self, slave, register, value):
        if stringp(value):
            if "." in value:
                self.writeWord(slave, register, value)
            else:
                self.writeSingleRegister(slave, register, value)
        elif isinstance(value, int):
            self.writeSingleRegister(slave, register, value)
        elif isinstance(value, float):
            self.writeWord(slave, register, value)

    # function 6 (Write Single Holding Register)
    def writeSingleRegister(self, slave, register, value):
        #        _logger.debug("writeSingleRegister(%d,%d,%d)" % (slave,register,value))
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_register(int(register),
                                       int(value),
                                       unit=int(slave))
            time.sleep(.03)  # avoid possible hickups on startup
        except Exception as ex:
            #            _logger.debug("writeSingleRegister exception: %s" % str(ex))
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeSingleRegister() {0}").format(str(ex)),
                exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 22 (Mask Write Register)
    # bits to be modified are "masked" with a 0 in the and_mask (not and_mask)
    # new bit values to be written are taken from the or_mask
    def maskWriteRegister(self, slave, register, and_mask, or_mask):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.mask_write_register(int(register),
                                            int(and_mask),
                                            int(or_mask),
                                            unit=int(slave))
            time.sleep(.03)
        except Exception as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeMask() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # a local variant of function 22 (Mask Write Register)
    # the masks are evaluated locally on the given integer value and the result is send via
    # using function 6
    def localMaskWriteRegister(self, slave, register, and_mask, or_mask,
                               value):
        new_val = (int(round(value)) & and_mask) | (or_mask &
                                                    (and_mask ^ 0xFFFF))
        self.writeSingleRegister(slave, register, int(new_val))

    # function 16 (Write Multiple Holding Registers)
    # values is a list of integers or one integer
    def writeRegisters(self, slave, register, values):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_registers(int(register), values, unit=int(slave))
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeRegisters() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 16 (Write Multiple Holding Registers)
    # value=int or float
    # writes a single precision 32bit float (2-registers)
    def writeWord(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            builder = getBinaryPayloadBuilder(self.byteorderLittle,
                                              self.wordorderLittle)
            builder.add_32bit_float(float(value))
            payload = builder.build()  # .tolist()
            self.master.write_registers(int(register),
                                        payload,
                                        unit=int(slave),
                                        skip_encode=True)
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeWord() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # translates given int value int a 16bit BCD and writes it into one register
    def writeBCD(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            builder = getBinaryPayloadBuilder(self.byteorderLittle,
                                              self.wordorderLittle)
            r = convert_to_bcd(int(value))
            builder.add_16bit_uint(r)
            payload = builder.build()  # .tolist()
            self.master.write_registers(int(register),
                                        payload,
                                        unit=int(slave),
                                        skip_encode=True)
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeWord() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 3 (Read Multiple Holding Registers) and 4 (Read Input Registers)
    def readFloat(self, slave, register, code=3):
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if code in self.readingsCache and slave in self.readingsCache[code] and register in self.readingsCache[code][slave] \
                and register+1 in self.readingsCache[code][slave]:
                # cache hit
                res = [
                    self.readingsCache[code][slave][register],
                    self.readingsCache[code][slave][register + 1]
                ]
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res, self.byteorderLittle, self.wordorderLittle)
                return decoder.decode_32bit_float()
            else:
                retry = self.readRetries
                while True:
                    if code == 3:
                        res = self.master.read_holding_registers(
                            int(register), 2, unit=int(slave))
                    else:
                        res = self.master.read_input_registers(int(register),
                                                               2,
                                                               unit=int(slave))
                    if res is None or res.isError(
                    ):  # requires pymodbus v1.5.1
                        if retry > 0:
                            retry = retry - 1
                            #time.sleep(0.020)
                        else:
                            raise Exception("Exception response")
                    else:
                        break
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res.registers, self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_32bit_float()
                if self.commError:  # we clear the previous error and send a message
                    self.commError = False
                    self.adderror(
                        QApplication.translate("Error Message",
                                               "Modbus Communication Resumed",
                                               None))
                return r
        except Exception:  # as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readFloat() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            if self.aw.seriallogflag:
                ser_str = "MODBUS readFloat :{},{},{},{},{},{} || Slave = {} || Register = {} || Code = {} || Rx = {}".format(
                    self.comport, self.baudrate, self.bytesize, self.parity,
                    self.stopbits, self.timeout, slave, register, code, r)
                self.addserial(ser_str)

    # function 3 (Read Multiple Holding Registers) and 4 (Read Input Registers)
    def readBCD(self, slave, register, code=3):
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if code in self.readingsCache and slave in self.readingsCache[code] and register in self.readingsCache[code][slave] \
                and register+1 in self.readingsCache[code][slave]:
                # cache hit
                res = [
                    self.readingsCache[code][slave][register],
                    self.readingsCache[code][slave][register + 1]
                ]
                decoder = getBinaryPayloadDecoderFromRegisters(
                    [res], self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_32bit_uint()
                return convert_from_bcd(r)
            else:
                retry = self.readRetries
                while True:
                    if code == 3:
                        res = self.master.read_holding_registers(
                            int(register), 2, unit=int(slave))
                    else:
                        res = self.master.read_input_registers(int(register),
                                                               2,
                                                               unit=int(slave))
                    if res is None or res.isError(
                    ):  # requires pymodbus v1.5.1
                        if retry > 0:
                            retry = retry - 1
                            #time.sleep(0.020)
                        else:
                            raise Exception("Exception response")
                    else:
                        break
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res.registers, self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_32bit_uint()
                if self.commError:  # we clear the previous error and send a message
                    self.commError = False
                    self.adderror(
                        QApplication.translate("Error Message",
                                               "Modbus Communication Resumed",
                                               None))
                time.sleep(
                    0.020
                )  # we add a small sleep between requests to help out the slow Loring electronic
                return convert_from_bcd(r)
        except Exception:  # as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readBCD() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            if self.aw.seriallogflag:
                ser_str = "MODBUS readBCD : {},{},{},{},{},{} || Slave = {} || Register = {} || Code = {} || Rx = {}".format(
                    self.comport, self.baudrate, self.bytesize, self.parity,
                    self.stopbits, self.timeout, slave, register, code, r)
                self.addserial(ser_str)

    # as readSingleRegister, but does not retry nor raise and error and returns a None instead
    # also does not reserve the port via a semaphore!
    def peekSingleRegister(self, slave, register, code=3):
        try:
            if code == 1:
                res = self.master.read_coils(int(register), 1, unit=int(slave))
            elif code == 2:
                res = self.master.read_discrete_inputs(int(register),
                                                       1,
                                                       unit=int(slave))
            elif code == 4:
                res = self.master.read_input_registers(int(register),
                                                       1,
                                                       unit=int(slave))
            else:  # code==3
                res = self.master.read_holding_registers(int(register),
                                                         1,
                                                         unit=int(slave))
        except Exception:
            res = None
        if res is not None and not res.isError():  # requires pymodbus v1.5.1
            if code in [1, 2]:
                if res is not None and res.bits[0]:
                    return 1
                else:
                    return 0
            else:
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res.registers, self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_16bit_uint()
                return r
        else:
            return None

    # function 1 (Read Coil)
    # function 2 (Read Discrete Input)
    # function 3 (Read Multiple Holding Registers) and
    # function 4 (Read Input Registers)
    def readSingleRegister(self, slave, register, code=3):
        #        import logging
        #        logging.basicConfig()
        #        log = logging.getLogger()
        #        log.setLevel(logging.DEBUG)
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            if code in self.readingsCache and slave in self.readingsCache[
                    code] and register in self.readingsCache[code][slave]:
                # cache hit
                res = self.readingsCache[code][slave][register]
                decoder = getBinaryPayloadDecoderFromRegisters(
                    [res], self.byteorderLittle, self.wordorderLittle)
                return decoder.decode_16bit_uint()
            else:
                retry = self.readRetries
                while True:
                    try:
                        if code == 1:
                            res = self.master.read_coils(int(register),
                                                         1,
                                                         unit=int(slave))
                        elif code == 2:
                            res = self.master.read_discrete_inputs(
                                int(register), 1, unit=int(slave))
                        elif code == 4:
                            res = self.master.read_input_registers(
                                int(register), 1, unit=int(slave))
                        else:  # code==3
                            res = self.master.read_holding_registers(
                                int(register), 1, unit=int(slave))
                    except Exception:
                        res = None
                    if res is None or res.isError(
                    ):  # requires pymodbus v1.5.1
                        if retry > 0:
                            retry = retry - 1
                            time.sleep(0.020)
                        else:
                            raise Exception("Exception response")
                    else:
                        break
                if code in [1, 2]:
                    if res is not None and res.bits[0]:
                        r = 1
                    else:
                        r = 0
                    if self.commError:  # we clear the previous error and send a message
                        self.commError = False
                        self.adderror(
                            QApplication.translate(
                                "Error Message",
                                "Modbus Communication Resumed", None))
                    return r
                else:
                    decoder = getBinaryPayloadDecoderFromRegisters(
                        res.registers, self.byteorderLittle,
                        self.wordorderLittle)
                    r = decoder.decode_16bit_uint()
                    if self.commError:  # we clear the previous error and send a message
                        self.commError = False
                        self.adderror(
                            QApplication.translate(
                                "Error Message",
                                "Modbus Communication Resumed", None))
                    return r
        except Exception:  # as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readSingleRegister() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            if self.aw.seriallogflag:
                ser_str = "MODBUS readSingleRegister : {},{},{},{},{},{} || Slave = {} || Register = {} || Code = {} || Rx = {}".format(
                    self.comport, self.baudrate, self.bytesize, self.parity,
                    self.stopbits, self.timeout, slave, register, code, r)
                self.addserial(ser_str)

    def setTarget(self, sv):
        if self.PID_slave_ID:
            multiplier = 1.
            if self.SVmultiplier == 1:
                multiplier = 10.
            elif self.SVmultiplier == 2:
                multiplier = 100.
            self.writeSingleRegister(self.PID_slave_ID, self.PID_SV_register,
                                     int(round(sv * multiplier)))

    def setPID(self, p, i, d):
        if self.PID_slave_ID and not (self.PID_p_register ==
                                      self.PID_i_register ==
                                      self.PID_d_register == 0):
            multiplier = 1.
            if self.PIDmultiplier == 1:
                multiplier = 10.
            elif self.PIDmultiplier == 2:
                multiplier = 100.
            self.writeSingleRegister(self.PID_slave_ID, self.PID_p_register,
                                     p * multiplier)
            self.writeSingleRegister(self.PID_slave_ID, self.PID_i_register,
                                     i * multiplier)
            self.writeSingleRegister(self.PID_slave_ID, self.PID_d_register,
                                     d * multiplier)
Exemple #29
0
    def __init__(self, sendmessage, adderror, addserial):
        self.sendmessage = sendmessage  # function to create an Artisan message to the user in the message line
        self.adderror = adderror  # signal an error to the user
        self.addserial = addserial  # add to serial log

        # retries
        self.readRetries = 1
        #default initial settings. They are changed by settingsload() at initiation of program acording to the device chosen
        self.comport = "COM5"  #NOTE: this string should not be translated.
        self.baudrate = 115200
        self.bytesize = 8
        self.parity = 'N'
        self.stopbits = 1
        self.timeout = 1.0
        self.PID_slave_ID = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""
        self.input1slave = 0
        self.input1register = 0
        self.input1float = False
        self.input1bcd = False
        self.input1code = 3
        self.input1div = 0  # 0: none, 1: 1/10, 2:1/100
        self.input1mode = "C"
        self.input2slave = 0
        self.input2register = 0
        self.input2float = False
        self.input2bcd = False
        self.input2code = 3
        self.input2div = 0
        self.input2mode = "C"
        self.input3slave = 0
        self.input3register = 0
        self.input3float = False
        self.input3bcd = False
        self.input3code = 3
        self.input3div = 0
        self.input3mode = "C"
        self.input4slave = 0
        self.input4register = 0
        self.input4float = False
        self.input4bcd = False
        self.input4code = 3
        self.input4div = 0
        self.input4mode = "C"
        self.input5slave = 0
        self.input5register = 0
        self.input5float = False
        self.input5bcd = False
        self.input5code = 3
        self.input5div = 0
        self.input5mode = "C"
        self.input6slave = 0
        self.input6register = 0
        self.input6float = False
        self.input6bcd = False
        self.input6code = 3
        self.input6div = 0
        self.input6mode = "C"
        self.SVmultiplier = 0
        self.PIDmultiplier = 0
        self.byteorderLittle = False
        self.wordorderLittle = True
        self.master = None
        self.COMsemaphore = QSemaphore(1)
        self.host = '127.0.0.1'  # the TCP/UDP host
        self.port = 502  # the TCP/UDP port
        self.type = 0
        # type =
        #    0: Serial RTU
        #    1: Serial ASCII
        #    2: Serial Binary
        #    3: TCP
        #    4: UDP
        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider Modbus actions with "read" command

        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data
Exemple #30
0
class modbusport(object):
    """ this class handles the communications with all the modbus devices"""
    def __init__(self, sendmessage, adderror, addserial):
        self.sendmessage = sendmessage  # function to create an Artisan message to the user in the message line
        self.adderror = adderror  # signal an error to the user
        self.addserial = addserial  # add to serial log

        # retries
        self.readRetries = 1
        #default initial settings. They are changed by settingsload() at initiation of program acording to the device chosen
        self.comport = "COM5"  #NOTE: this string should not be translated.
        self.baudrate = 115200
        self.bytesize = 8
        self.parity = 'N'
        self.stopbits = 1
        self.timeout = 1.0
        self.PID_slave_ID = 0
        self.PID_SV_register = 0
        self.PID_p_register = 0
        self.PID_i_register = 0
        self.PID_d_register = 0
        self.PID_ON_action = ""
        self.PID_OFF_action = ""
        self.input1slave = 0
        self.input1register = 0
        self.input1float = False
        self.input1bcd = False
        self.input1code = 3
        self.input1div = 0  # 0: none, 1: 1/10, 2:1/100
        self.input1mode = "C"
        self.input2slave = 0
        self.input2register = 0
        self.input2float = False
        self.input2bcd = False
        self.input2code = 3
        self.input2div = 0
        self.input2mode = "C"
        self.input3slave = 0
        self.input3register = 0
        self.input3float = False
        self.input3bcd = False
        self.input3code = 3
        self.input3div = 0
        self.input3mode = "C"
        self.input4slave = 0
        self.input4register = 0
        self.input4float = False
        self.input4bcd = False
        self.input4code = 3
        self.input4div = 0
        self.input4mode = "C"
        self.input5slave = 0
        self.input5register = 0
        self.input5float = False
        self.input5bcd = False
        self.input5code = 3
        self.input5div = 0
        self.input5mode = "C"
        self.input6slave = 0
        self.input6register = 0
        self.input6float = False
        self.input6bcd = False
        self.input6code = 3
        self.input6div = 0
        self.input6mode = "C"
        self.SVmultiplier = 0
        self.PIDmultiplier = 0
        self.byteorderLittle = False
        self.wordorderLittle = True
        self.master = None
        self.COMsemaphore = QSemaphore(1)
        self.host = '127.0.0.1'  # the TCP/UDP host
        self.port = 502  # the TCP/UDP port
        self.type = 0
        # type =
        #    0: Serial RTU
        #    1: Serial ASCII
        #    2: Serial Binary
        #    3: TCP
        #    4: UDP
        self.lastReadResult = 0  # this is set by eventaction following some custom button/slider Modbus actions with "read" command

        self.commError = False  # True after a communication error was detected and not yet cleared by receiving proper data

    # this garantees a minimum of 30 miliseconds between readings and 80ms between writes (according to the Modbus spec) on serial connections
    # this sleep delays between requests seems to be beneficial on slow RTU serial connections like those of the FZ-94
    def sleepBetween(self, write=False):
        if write:
            #            if self.type in [3,4]: # TCP or UDP
            #                time.sleep(0.040)
            pass  # handled in MODBUS lib
            #            else:
            time.sleep(0.035)
        else:
            if self.type in [
                    3, 4
            ]:  # delay between writes only on serial connections
                pass
            else:
                time.sleep(0.035)

    def address2register(self, addr, code=3):
        if code == 3 or code == 6:
            return addr - 40001
        else:
            return addr - 30001

    def isConnected(self):
        return not (self.master is None) and self.master.socket

    def disconnect(self):
        try:
            self.master.close()
        except Exception:
            pass
        self.master = None

    def connect(self):
        #        if self.master and not self.master.socket:
        #            self.master = None
        if self.master is None:
            self.commError = False
            try:
                # as in the following the port is None, no port is opened on creation of the (py)serial object
                if self.type == 1:  # Serial ASCII
                    from pymodbus.client.sync import ModbusSerialClient
                    self.master = ModbusSerialClient(method='ascii',
                                                     port=self.comport,
                                                     baudrate=self.baudrate,
                                                     bytesize=self.bytesize,
                                                     parity=self.parity,
                                                     stopbits=self.stopbits,
                                                     retry_on_empty=True,
                                                     timeout=self.timeout)
                elif self.type == 2:  # Serial Binary
                    from pymodbus.client.sync import ModbusSerialClient
                    self.master = ModbusSerialClient(method='binary',
                                                     port=self.comport,
                                                     baudrate=self.baudrate,
                                                     bytesize=self.bytesize,
                                                     parity=self.parity,
                                                     stopbits=self.stopbits,
                                                     retry_on_empty=True,
                                                     timeout=self.timeout)
                elif self.type == 3:  # TCP
                    from pymodbus.client.sync import ModbusTcpClient
                    try:
                        self.master = ModbusTcpClient(
                            host=self.host,
                            port=self.port,
                            retry_on_empty=True,
                            retries=1,
                            timeout=0.9,  #self.timeout
                        )
                        self.readRetries = 0
                    except:
                        self.master = ModbusTcpClient(
                            host=self.host,
                            port=self.port,
                        )
                elif self.type == 4:  # UDP
                    from pymodbus.client.sync import ModbusUdpClient
                    try:
                        self.master = ModbusUdpClient(
                            host=self.host,
                            port=self.port,
                            retry_on_empty=True,
                            retries=3,
                            timeout=0.7,  #self.timeout
                        )
                    except:  # older versions of pymodbus don't support the retries, timeout nor the retry_on_empty arguments
                        self.master = ModbusUdpClient(
                            host=self.host,
                            port=self.port,
                        )
                else:  # Serial RTU
                    from pymodbus.client.sync import ModbusSerialClient
                    self.master = ModbusSerialClient(method='rtu',
                                                     port=self.comport,
                                                     baudrate=self.baudrate,
                                                     bytesize=self.bytesize,
                                                     parity=self.parity,
                                                     stopbits=self.stopbits,
                                                     retry_on_empty=False,
                                                     timeout=self.timeout)
                    self.readRetries = 1
                self.master.connect()
                self.adderror(
                    QApplication.translate("Error Message",
                                           "Connected via MODBUS", None))
                time.sleep(.5)  # avoid possible hickups on startup
            except Exception as ex:
                _, _, exc_tb = sys.exc_info()
                self.adderror(
                    (QApplication.translate("Error Message", "Modbus Error:",
                                            None) + " connect() {0}").format(
                                                str(ex)), exc_tb.tb_lineno)

    # function 15 (Write Multiple Coils)
    def writeCoils(self, slave, register, values):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_coils(int(register),
                                    list(values),
                                    unit=int(slave))
            time.sleep(.3)  # avoid possible hickups on startup
        except Exception as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeCoils() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 5 (Write Single Coil)
    def writeCoil(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_coil(int(register), value, unit=int(slave))
            time.sleep(.3)  # avoid possible hickups on startup
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeCoil() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # write value to register on slave (function 6 for int or function 16 for float)
    # value can be one of string (containing an int or float), an int or a float
    def writeRegister(self, slave, register, value):
        #        print("writeRegister",slave,register,value)
        if stringp(value):
            if "." in value:
                self.writeWord(slave, register, value)
            else:
                self.writeSingleRegister(slave, register, value)
        elif isinstance(value, int):
            self.writeSingleRegister(slave, register, value)
        elif isinstance(value, float):
            self.writeWord(slave, register, value)

    # function 6 (Write Single Holding Register)
    def writeSingleRegister(self, slave, register, value):
        #        _logger.debug("writeSingleRegister(%d,%d,%d)" % (slave,register,value))
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_register(int(register),
                                       int(value),
                                       unit=int(slave))
            time.sleep(.03)  # avoid possible hickups on startup
        except Exception as ex:
            #            _logger.debug("writeSingleRegister exception: %s" % str(ex))
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeSingleRegister() {0}").format(str(ex)),
                exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 22 (Mask Write Register)
    def maskWriteRegister(self, slave, register, and_mask, or_mask):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.mask_write_register(int(register),
                                            int(and_mask),
                                            int(or_mask),
                                            unit=int(slave))
            time.sleep(.03)
        except Exception as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeMask() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 16 (Write Multiple Holding Registers)
    # values is a list of integers or one integer
    def writeRegisters(self, slave, register, values):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            self.master.write_registers(int(register), values, unit=int(slave))
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeRegisters() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 16 (Write Multiple Holding Registers)
    # value=int or float
    # writes a single precision 32bit float (2-registers)
    def writeWord(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            builder = getBinaryPayloadBuilder(self.byteorderLittle,
                                              self.wordorderLittle)
            builder.add_32bit_float(float(value))
            payload = builder.build()  # .tolist()
            self.master.write_registers(int(register),
                                        payload,
                                        unit=int(slave),
                                        skip_encode=True)
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeWord() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # translates given int value int a 16bit BCD and writes it into one register
    def writeBCD(self, slave, register, value):
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            builder = getBinaryPayloadBuilder(self.byteorderLittle,
                                              self.wordorderLittle)
            r = convert_to_bcd(int(value))
            builder.add_16bit_uint(r)
            payload = builder.build()  # .tolist()
            self.master.write_registers(int(register),
                                        payload,
                                        unit=int(slave),
                                        skip_encode=True)
            time.sleep(.03)
        except Exception as ex:
            #            self.disconnect()
            _, _, exc_tb = sys.exc_info()
            self.adderror(
                (QApplication.translate("Error Message", "Modbus Error:", None)
                 + " writeWord() {0}").format(str(ex)), exc_tb.tb_lineno)
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)

    # function 3 (Read Multiple Holding Registers) and 4 (Read Input Registers)
    def readFloat(self, slave, register, code=3):
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            retry = self.readRetries
            while True:
                if code == 3:
                    res = self.master.read_holding_registers(int(register),
                                                             2,
                                                             unit=int(slave))
                else:
                    res = self.master.read_input_registers(int(register),
                                                           2,
                                                           unit=int(slave))
                if res is None or res.isError():  # requires pymodbus v1.5.1
                    if retry > 0:
                        retry = retry - 1
                        #time.sleep(0.020)
                    else:
                        raise Exception("Exception response")
                else:
                    break
            decoder = getBinaryPayloadDecoderFromRegisters(
                res.registers, self.byteorderLittle, self.wordorderLittle)
            r = decoder.decode_32bit_float()
            if self.commError:  # we clear the previous error and send a message
                self.commError = False
                self.adderror(
                    QApplication.translate("Error Message",
                                           "Modbus Communication Resumed",
                                           None))
            return r
        except Exception:  # as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readFloat() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            settings = str(self.comport) + "," + str(
                self.baudrate) + "," + str(self.bytesize) + "," + str(
                    self.parity) + "," + str(self.stopbits) + "," + str(
                        self.timeout)
            ser_str = "MODBUS readFloat :" + settings + " || Slave = " + str(
                slave) + " || Register = " + str(
                    register) + " || Code = " + str(code)
            if r is not None:
                ser_str = ser_str + " || Rx = " + str(r)
            self.addserial(ser_str)

    # function 3 (Read Multiple Holding Registers) and 4 (Read Input Registers)
    def readBCD(self, slave, register, code=3):
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            retry = self.readRetries
            while True:
                if code == 3:
                    res = self.master.read_holding_registers(int(register),
                                                             1,
                                                             unit=int(slave))
                else:
                    res = self.master.read_input_registers(int(register),
                                                           1,
                                                           unit=int(slave))
                if res is None or res.isError():  # requires pymodbus v1.5.1
                    if retry > 0:
                        retry = retry - 1
                        #time.sleep(0.020)
                    else:
                        raise Exception("Exception response")
                else:
                    break
            decoder = getBinaryPayloadDecoderFromRegisters(
                res.registers, self.byteorderLittle, self.wordorderLittle)
            r = decoder.decode_16bit_uint()
            if self.commError:  # we clear the previous error and send a message
                self.commError = False
                self.adderror(
                    QApplication.translate("Error Message",
                                           "Modbus Communication Resumed",
                                           None))
            time.sleep(
                0.020
            )  # we add a small sleep between requests to help out the slow Loring electronic
            return convert_from_bcd(r)
        except Exception:  # as ex:
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            self.disconnect()
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readBCD() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            settings = str(self.comport) + "," + str(
                self.baudrate) + "," + str(self.bytesize) + "," + str(
                    self.parity) + "," + str(self.stopbits) + "," + str(
                        self.timeout)
            ser_str = "MODBUS readBCD :" + settings + " || Slave = " + str(
                slave) + " || Register = " + str(
                    register) + " || Code = " + str(code)
            if r is not None:
                ser_str = ser_str + " || Rx = " + str(r)
            self.addserial(ser_str)

    # as readSingleRegister, but does not retry nor raise and error and returns a None instead
    # also does not reserve the port via a semaphore!
    def peekSingleRegister(self, slave, register, code=3):
        try:
            if code == 1:
                res = self.master.read_coils(int(register), 1, unit=int(slave))
            elif code == 2:
                res = self.master.read_discrete_inputs(int(register),
                                                       1,
                                                       unit=int(slave))
            elif code == 4:
                res = self.master.read_input_registers(int(register),
                                                       1,
                                                       unit=int(slave))
            else:  # code==3
                res = self.master.read_holding_registers(int(register),
                                                         1,
                                                         unit=int(slave))
        except Exception:
            res = None
        if res is not None and not res.isError():  # requires pymodbus v1.5.1
            if code in [1, 2]:
                if res is not None and res.bits[0]:
                    return 1
                else:
                    return 0
            else:
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res.registers, self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_16bit_uint()
                return r
        else:
            return None

    # function 1 (Read Coil)
    # function 2 (Read Discrete Input)
    # function 3 (Read Multiple Holding Registers) and
    # function 4 (Read Input Registers)
    def readSingleRegister(self, slave, register, code=3):
        #        import logging
        #        logging.basicConfig()
        #        log = logging.getLogger()
        #        log.setLevel(logging.DEBUG)
        r = None
        try:
            #### lock shared resources #####
            self.COMsemaphore.acquire(1)
            self.connect()
            retry = self.readRetries
            while True:
                try:
                    if code == 1:
                        res = self.master.read_coils(int(register),
                                                     1,
                                                     unit=int(slave))
                    elif code == 2:
                        res = self.master.read_discrete_inputs(int(register),
                                                               1,
                                                               unit=int(slave))
                    elif code == 4:
                        res = self.master.read_input_registers(int(register),
                                                               1,
                                                               unit=int(slave))
                    else:  # code==3
                        res = self.master.read_holding_registers(
                            int(register), 1, unit=int(slave))
                except Exception:
                    res = None
                if res is None or res.isError():  # requires pymodbus v1.5.1
                    if retry > 0:
                        retry = retry - 1
                        time.sleep(0.020)
                    else:
                        raise Exception("Exception response")
                else:
                    break
            if code in [1, 2]:
                if res is not None and res.bits[0]:
                    r = 1
                else:
                    r = 0
                return r
            else:
                decoder = getBinaryPayloadDecoderFromRegisters(
                    res.registers, self.byteorderLittle, self.wordorderLittle)
                r = decoder.decode_16bit_uint()
                if self.commError:  # we clear the previous error and send a message
                    self.commError = False
                    self.adderror(
                        QApplication.translate("Error Message",
                                               "Modbus Communication Resumed",
                                               None))
                return r
        except Exception:  # as ex:
            #            self.disconnect()
            #            import traceback
            #            traceback.print_exc(file=sys.stdout)
            #            _, _, exc_tb = sys.exc_info()
            #            self.adderror((QApplication.translate("Error Message","Modbus Error:",None) + " readSingleRegister() {0}").format(str(ex)),exc_tb.tb_lineno)
            self.adderror(
                QApplication.translate("Error Message",
                                       "Modbus Communication Error", None))
            self.commError = True
        finally:
            if self.COMsemaphore.available() < 1:
                self.COMsemaphore.release(1)
            #note: logged chars should be unicode not binary
            settings = str(self.comport) + "," + str(
                self.baudrate) + "," + str(self.bytesize) + "," + str(
                    self.parity) + "," + str(self.stopbits) + "," + str(
                        self.timeout)
            ser_str = "MODBUS readSingleRegister :" + settings + " || Slave = " + str(
                slave) + " || Register = " + str(
                    register) + " || Code = " + str(code)
            if r is not None:
                ser_str = ser_str + " || Rx = " + str(r)
            self.addserial(ser_str)

    def setTarget(self, sv):
        if self.PID_slave_ID:
            multiplier = 1.
            if self.SVmultiplier == 1:
                multiplier = 10.
            elif self.SVmultiplier == 2:
                multiplier = 100.
            self.writeSingleRegister(self.PID_slave_ID, self.PID_SV_register,
                                     int(round(sv * multiplier)))

    def setPID(self, p, i, d):
        if self.PID_slave_ID and not (self.PID_p_register ==
                                      self.PID_i_register ==
                                      self.PID_d_register == 0):
            multiplier = 1.
            if self.PIDmultiplier == 1:
                multiplier = 10.
            elif self.PIDmultiplier == 2:
                multiplier = 100.
            self.writeSingleRegister(self.PID_slave_ID, self.PID_p_register,
                                     p * multiplier)
            self.writeSingleRegister(self.PID_slave_ID, self.PID_i_register,
                                     i * multiplier)
            self.writeSingleRegister(self.PID_slave_ID, self.PID_d_register,
                                     d * multiplier)