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
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
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")
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")
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
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()
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 __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
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 __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)
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()
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)
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()
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)
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)
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
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))
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):
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")
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)
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:
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)
# 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:
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)
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
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)