First version for server&client
This commit is contained in:
parent
d55027d422
commit
53774cae46
@ -20,7 +20,7 @@ class MCP4822:
|
||||
'''
|
||||
Constructor of the class, setup the GPIO pin for SPI-communication
|
||||
'''
|
||||
def __init__(self, CLK_Pin, MOSI_Pin, CS_Pin, LDAC_Pin, vref=None):
|
||||
def __init__(self, CLK_Pin, MOSI_Pin, CS_Pin, LDAC_Pin, nullVoltage=0, vref=None):
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
|
||||
self.clkPin = CLK_Pin
|
||||
@ -43,7 +43,8 @@ class MCP4822:
|
||||
GPIO.output(self.mosiPin, GPIO.LOW)
|
||||
GPIO.output(self.ldacPin, GPIO.LOW)
|
||||
|
||||
self.write(1.29,1)
|
||||
self.nullVoltage = nullVoltage
|
||||
self.write(self.nullVoltage,1)
|
||||
|
||||
def U2dac(self,U,DAC):
|
||||
#U: voltage (V) [0V-4.096V]
|
||||
|
||||
219
src/PID.py
Normal file
219
src/PID.py
Normal file
@ -0,0 +1,219 @@
|
||||
'''
|
||||
Created on Tue Jan 9 2018
|
||||
|
||||
@author: jan
|
||||
'''
|
||||
|
||||
import time
|
||||
import globalvars
|
||||
import queue
|
||||
import zmq
|
||||
|
||||
class PID:
|
||||
def __init__(self, dev_num_adc, channel_num_adc, dev_num_dac, channel_num_dac, pid_num, kp=None, ki=None, kd=None, setpoint=None):
|
||||
if kp is None:
|
||||
self.kp = 1
|
||||
else:
|
||||
self.kp = float(kp)
|
||||
|
||||
if ki is None:
|
||||
self.ki = 1
|
||||
else:
|
||||
self.ki = float(ki)
|
||||
|
||||
if kd is None:
|
||||
self.kd = 1
|
||||
else:
|
||||
self.kd = float(kd)
|
||||
|
||||
if setpoint is None:
|
||||
self.setpoint = 0.0
|
||||
else:
|
||||
self.setpoint = float(setpoint)
|
||||
|
||||
self.dev_num_adc = dev_num_adc
|
||||
self.channel_num_adc = channel_num_adc
|
||||
self.dev_num_dac = dev_num_dac
|
||||
self.channel_dac = channel_num_dac
|
||||
self.pid_num = pid_num
|
||||
|
||||
self.ITerm = 0
|
||||
self.lastInput = self.setpoint
|
||||
self.outMin = 0
|
||||
self.outMax = 5
|
||||
self.error = 0
|
||||
|
||||
self.start = time.time()
|
||||
self.stop = 0
|
||||
|
||||
# Order of initialisation shouldn't matter, because zmq handel everything
|
||||
self.context = zmq.Context(1)
|
||||
self.SPIclient = self.context.socket(zmq.REQ)
|
||||
self.SPIclient.connect("tcp://localhost:5555")
|
||||
|
||||
def computePID(self):
|
||||
#Get ADC value
|
||||
#self.out_queue.put(['ADC', self.dev_num, self.channel_num, self.pid_num])
|
||||
#Input = self.in_queue.get()
|
||||
msg = ['ADC', self.dev_num_adc, self.channel_num_adc, self.pid_num]
|
||||
self.SPIclient.send('\t'.join(str(x) for x in msg).encode())
|
||||
Input = float(self.SPIclient.recv().decode())
|
||||
|
||||
#Get elapsed time for right PID constants
|
||||
self.stop = time.time()
|
||||
elapsed_time = self.stop - self.start
|
||||
self.start = time.time()
|
||||
|
||||
#print(type(self.ki))
|
||||
Ki = float(self.ki) * elapsed_time
|
||||
Kd = float(self.kd) / elapsed_time
|
||||
|
||||
#Calculate working variables
|
||||
self.error = float(self.setpoint) - Input
|
||||
dInput = Input - self.lastInput
|
||||
self.ITerm = self.ITerm + (Ki * self.error)
|
||||
# Check min-max for I-Term
|
||||
if self.ITerm > self.outMax:
|
||||
self.ITerm = self.outMax
|
||||
elif self.ITerm < self.outMin:
|
||||
self.ITerm = self.outMin
|
||||
|
||||
# Calculate PID Output
|
||||
self.Output = float(self.kp) * self.error + self.ITerm - Kd * dInput
|
||||
# Check min-max for output
|
||||
if self.Output > self.outMax:
|
||||
self.Output = self.outMax
|
||||
elif self.Output < self.outMin:
|
||||
self.Output = self.outMin
|
||||
|
||||
# Save variable for next run
|
||||
self.lastInput = Input
|
||||
|
||||
# Set output value
|
||||
#self.out_queue.put(['DAC', self.dev_num, self.channel_num, self.pid_num, self.Output])
|
||||
msg = ['DAC', self.dev_num_dac, self.channel_num_dac, self.pid_num, self.Output]
|
||||
self.SPIclient.send('\t'.join(str(x) for x in msg).encode())
|
||||
ans = self.SPIclient.recv().decode()
|
||||
#if self.in_queue.get() != 'ACK':
|
||||
if ans != 'ACK':
|
||||
print('Error setting DAC-value')
|
||||
|
||||
def setSetpoint(self, setpoint):
|
||||
#self.ITerm = 0
|
||||
self.setpoint = setpoint
|
||||
|
||||
def setKp(self,P):
|
||||
self.kp=P
|
||||
|
||||
def setKi(self,I):
|
||||
self.ki=I
|
||||
|
||||
def setKd(self,D):
|
||||
self.kd=D
|
||||
|
||||
def setOutmax(self,outMax):
|
||||
self.outMax = outMax
|
||||
|
||||
def getSetpoint(self):
|
||||
return self.setpoint
|
||||
|
||||
def getError(self):
|
||||
return self.error
|
||||
|
||||
def getKp(self):
|
||||
return self.kp
|
||||
|
||||
def getKi(self):
|
||||
return self.ki
|
||||
|
||||
def getKd(self):
|
||||
return self.kd
|
||||
|
||||
def getOutput(self):
|
||||
return self.Output
|
||||
|
||||
def getTemperatur(self):
|
||||
return self.lastInput
|
||||
|
||||
def getRunValues(self):
|
||||
return [self.lastInput, self.error, self.Output]
|
||||
|
||||
|
||||
def run(pid_obj, freq, num):
|
||||
context = zmq.Context()
|
||||
server = context.socket(zmq.REP)
|
||||
port = 5556 + num
|
||||
server.bind("tcp://*:%d" % port)
|
||||
poller = zmq.Poller()
|
||||
poller.register(server, zmq.POLLIN)
|
||||
|
||||
stop = False
|
||||
|
||||
while not globalvars.stopall and not stop:
|
||||
cmd_recv = ''
|
||||
if globalvars.pidrun[num]:
|
||||
pid_obj.computePID()
|
||||
#try:
|
||||
# msg = in_queue.get(True,1/freq)
|
||||
#except queue.Empty:
|
||||
# msg = ['None']
|
||||
socks = dict(poller.poll(1000))
|
||||
if server in socks and socks[server] == zmq.POLLIN:
|
||||
cmd_recv = server.recv_string()
|
||||
else:
|
||||
cmd_recv = 'None'
|
||||
else:
|
||||
socks = dict(poller.poll(1000))
|
||||
if server in socks and socks[server] == zmq.POLLIN:
|
||||
cmd_recv = server.recv_string()
|
||||
|
||||
msg = cmd_recv.split('\t')
|
||||
ans = 'ACK'
|
||||
if msg[0] == 'None':
|
||||
pass
|
||||
elif msg[0] == 'set_setpoint':
|
||||
pid_obj.setSetpoint(msg[1])
|
||||
server.send_string('ACK')
|
||||
elif msg[0] == 'set_kp':
|
||||
pid_obj.setKp(msg[1])
|
||||
server.send_string('ACK')
|
||||
elif msg[0] == 'set_ki':
|
||||
pid_obj.setKi(msg[1])
|
||||
server.send_string('ACK')
|
||||
elif msg[0] == 'set_kd':
|
||||
pid_obj.setKd(msg[1])
|
||||
server.send_string('ACK')
|
||||
elif msg[0] == 'set_outmax':
|
||||
pid_obj.setOutmax(msg[1])
|
||||
server.send_string('ACK')
|
||||
elif msg[0] == 'get_setpoint':
|
||||
#out_queue.put(pid_obj.getSetpoint() )
|
||||
server.send_string(str(pid_obj.getSetpoint()))
|
||||
elif msg[0] == 'get_error':
|
||||
#out_queue.put(pid_obj.getError() )
|
||||
server.send_string(str(pid_obj.getError()))
|
||||
elif msg[0] == 'get_kp':
|
||||
#out_queue.put(pid_obj.getKp() )
|
||||
server.send_string(str(pid_obj.getKp()))
|
||||
elif msg[0] == 'get_ki':
|
||||
#out_queue.put(pid_obj.getKi() )
|
||||
server.send_string(str(pid_obj.getKi()))
|
||||
elif msg[0] == 'get_kd':
|
||||
#out_queue.put(pid_obj.getKd() )
|
||||
server.send_string(str(pid_obj.getKd()))
|
||||
elif msg[0] == 'lock':
|
||||
print('Starting PID')
|
||||
server.send_string('Starting PID')
|
||||
elif msg[0] == 'get_output':
|
||||
#out_queue.put(pid_obj.getOutput() )
|
||||
server.send_string(str(pid_obj.getOutput()))
|
||||
elif msg[0] == 'get_temperatur':
|
||||
#out_queue.put(pid_obj.getTemperatur() )
|
||||
server.send_string(str(pid_obj.getTemperatur()))
|
||||
elif msg[0] == 'get_run_values':
|
||||
#out_queue.put(pid_obj.getRunValues() )
|
||||
server.send_string(str(pid_obj.getRunValues()))
|
||||
|
||||
elif msg[0] == 'END':
|
||||
server.send_string('ACK')
|
||||
stop = True
|
||||
210
src/clientthread.py
Normal file
210
src/clientthread.py
Normal file
@ -0,0 +1,210 @@
|
||||
import queue
|
||||
import socket
|
||||
|
||||
import zmq
|
||||
import globalvars
|
||||
import tcptools as TCP
|
||||
#TCP/IP client thread
|
||||
def clientthread(conn):
|
||||
global lock,busy
|
||||
welcom_msg = '--> Welcome to the Time-Bin-temperature controller <--\n'
|
||||
welcom_msg = welcom_msg + ' '*(1024-len(welcom_msg))
|
||||
conn.send(welcom_msg.encode())
|
||||
print('\nopen thread...')
|
||||
|
||||
#Connect to SPI thread via zmq tcp socket
|
||||
context = zmq.Context(1)
|
||||
SPIclient = context.socket(zmq.REQ)
|
||||
SPIclient.connect("tcp://localhost:5555")
|
||||
|
||||
#Connect to each pid threads
|
||||
pidclient = [0]
|
||||
context = zmq.Context(1)
|
||||
pidclient[0] = context.socket(zmq.REQ)
|
||||
pidclient[0].connect("tcp://localhost:5556")
|
||||
|
||||
|
||||
while not globalvars.stopall:
|
||||
try:
|
||||
cmd_recv = conn.recv(1024)
|
||||
except:
|
||||
print('\nSocket broken')
|
||||
break
|
||||
if not cmd_recv:
|
||||
break
|
||||
if globalvars.stopall:
|
||||
break
|
||||
|
||||
ans = ''
|
||||
|
||||
#split command
|
||||
tmp = cmd_recv.decode().split('\t')
|
||||
|
||||
try:
|
||||
tmp[2] = int(str2num(tmp[2]))
|
||||
tmp[3] = str2num(tmp[3])
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
#print(tmp)
|
||||
|
||||
if tmp[0] == 'CMD:':
|
||||
if tmp[1] == 'close':
|
||||
TCP.send_msg(conn,'ANS: Bye')
|
||||
break
|
||||
elif tmp[1] == 'IDLE':
|
||||
globalvars.pidrun[tmp[2]] = False
|
||||
TCP.send_msg(conn, 'I set PID {:.0f} to {:.0f}'.format(tmp[2],False))
|
||||
elif tmp[1] == 'LOCK':
|
||||
globalvars.pidrun[tmp[2]] = True
|
||||
#out_queue[tmp[2]].put(['lock'])
|
||||
pidclient[tmp[2]].send_string('lock')
|
||||
pidclient[tmp[2]].recv_string()
|
||||
TCP.send_msg(conn, 'I set PID {:.0f} to {:.0f}'.format(tmp[2],True))
|
||||
elif tmp[1] == 'SET_SETPOINT':
|
||||
#out_queue[tmp[2]].put(['set_setpoint',tmp[3]])
|
||||
pidclient[tmp[2]].send_string('set_setpoint\t%f' % tmp[3])
|
||||
pidclient[tmp[2]].recv_string()
|
||||
TCP.send_msg(conn, 'I set the setpoint for output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
|
||||
elif tmp[1] == 'SET_KP':
|
||||
#out_queue[tmp[2]].put(['set_kp',tmp[3]])
|
||||
pidclient[tmp[2]].send_string('set_kp\t%f' % tmp[3])
|
||||
pidclient[tmp[2]].recv_string()
|
||||
TCP.send_msg(conn, 'I set kp for output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
|
||||
elif tmp[1] == 'SET_KI':
|
||||
#out_queue[tmp[2]].put(['set_ki',tmp[3]])
|
||||
pidclient[tmp[2]].send_string('set_ki\t%f' % tmp[3])
|
||||
pidclient[tmp[2]].recv_string()
|
||||
TCP.send_msg(conn, 'I set ki for output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
|
||||
elif tmp[1] == 'SET_KD':
|
||||
#out_queue[tmp[2]].put(['set_kd',tmp[3]])
|
||||
pidclient[tmp[2]].send_string('set_kd\t%f' % tmp[3])
|
||||
pidclient[tmp[2]].recv_string()
|
||||
TCP.send_msg(conn, 'I set kd for output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
|
||||
elif tmp[1] == 'SET_OUTMAX':
|
||||
#out_queue[tmp[2]].put(['set_outmax',tmp[3]])
|
||||
pidclient[tmp[2]].send_string('set_outmax\t%f' % tmp[3])
|
||||
pidclient[tmp[2]].recv_string()
|
||||
TCP.send_msg(conn, 'I set maximal output voltage for output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
|
||||
elif tmp[1] == 'GET_SETPOINT':
|
||||
#out_queue[tmp[2]].put(['get_setpoint'])
|
||||
pidclient[tmp[2]].send_string('get_setpoint')
|
||||
ans = float(pidclient[tmp[2]].recv_string())
|
||||
#ans = in_queue[tmp[2]].get()
|
||||
TCP.send_msg(conn, ans)
|
||||
elif tmp[1] == 'GET_ERROR':
|
||||
#out_queue[tmp[2]].put(['get_error'])
|
||||
pidclient[tmp[2]].send_string('get_error')
|
||||
ans = float(pidclient[tmp[2]].recv_string())
|
||||
#ans = in_queue[tmp[2]].get()
|
||||
TCP.send_msg(conn, ans)
|
||||
elif tmp[1] == 'GET_KP':
|
||||
#out_queue[tmp[2]].put(['get_kp'])
|
||||
pidclient[tmp[2]].send_string('get_kp')
|
||||
ans = float(pidclient[tmp[2]].recv_string())
|
||||
#ans = in_queue[tmp[2]].get()
|
||||
TCP.send_msg(conn, ans)
|
||||
elif tmp[1] == 'GET_KI':
|
||||
#out_queue[tmp[2]].put(['get_ki'])
|
||||
pidclient[tmp[2]].send_string('get_ki')
|
||||
ans = float(pidclient[tmp[2]].recv_string())
|
||||
#ans = in_queue[tmp[2]].get()
|
||||
TCP.send_msg(conn, ans)
|
||||
elif tmp[1] == 'GET_KD':
|
||||
#out_queue[tmp[2]].put(['get_kd'])
|
||||
pidclient[tmp[2]].send_string('get_kd')
|
||||
ans = float(pidclient[tmp[2]].recv_string())
|
||||
#ans = in_queue[tmp[2]].get()
|
||||
TCP.send_msg(conn, ans)
|
||||
elif tmp[1] == 'GET_PID_STATUS':
|
||||
ans = int(globalvars.pidrun[tmp[2]])
|
||||
TCP.send_msg(conn, ans)
|
||||
elif tmp[1] == 'GET_TEMP':
|
||||
if globalvars.pidrun[tmp[2]]:
|
||||
#out_queue[tmp[2]].put(['get_temperatur'])
|
||||
pidclient[tmp[2]].send_string('get_temperatur')
|
||||
ans = float(pidclient[tmp[2]].recv_string())
|
||||
#ans = in_queue[tmp[2]].get()
|
||||
TCP.send_msg(conn, ans)
|
||||
else:
|
||||
if tmp[2] == 0:
|
||||
dev_chan = ['ADC', 0, 0, 4]
|
||||
|
||||
|
||||
#SPIQueue.put(dev_chan)
|
||||
SPIclient.send('\t'.join(str(x) for x in dev_chan).encode())
|
||||
ans = SPIclient.recv().decode()
|
||||
#ans = tcpqueue.get()
|
||||
TCP.send_msg(conn, ans)
|
||||
|
||||
elif tmp[1] == 'GET_RUN_VALUES':
|
||||
if globalvars.pidrun[tmp[2]]:
|
||||
#out_queue[tmp[2]].put(['get_run_values'])
|
||||
pidclient[tmp[2]].send_string('get_run_values')
|
||||
ans = eval(pidclient[tmp[2]].recv_string())
|
||||
#ans = in_queue[tmp[2]].get()
|
||||
TCP.send_msg(conn, ans)
|
||||
else:
|
||||
if tmp[2] == 0:
|
||||
dev_chan = ['ADC', 0, 0, 4]
|
||||
|
||||
|
||||
#SPIQueue.put(dev_chan)
|
||||
#ans = tcpqueue.get()
|
||||
SPIclient.send('\t'.join(str(x) for x in dev_chan).encode())
|
||||
ans = float(SPIclient.recv().decode())
|
||||
#adc value, 0, output voltage
|
||||
TCP.send_msg(conn, [ans, 0, globalvars.output_volt[tmp[2]]])
|
||||
|
||||
elif tmp[1] == 'GET_VOLTAGE':
|
||||
if globalvars.pidrun[tmp[2]]:
|
||||
#out_queue[tmp[2]].put(['get_output'])
|
||||
pidclient[tmp[2]].send_string('get_output')
|
||||
ans = float(pidclient[tmp[2]].recv_string())
|
||||
#ans = in_queue[tmp[2]].get()
|
||||
TCP.send_msg(conn, ans)
|
||||
globalvars.output_volt[tmp[2]] = ans
|
||||
else:
|
||||
TCP.send_msg(conn, globalvars.output_volt[tmp[2]])
|
||||
|
||||
elif tmp[1] == 'SET_VOLTAGE':
|
||||
if tmp[2] == 0:
|
||||
dev_chan = ['DAC', 0, 0, 4, tmp[3]]
|
||||
elif tmp[2] == 1:
|
||||
dev_chan = ['DAC', 0, 1, 4, tmp[3]]
|
||||
|
||||
|
||||
globalvars.output_volt[tmp[2]] = tmp[3]
|
||||
|
||||
#SPIQueue.put(dev_chan)
|
||||
#ans = tcpqueue.get()
|
||||
SPIclient.send('\t'.join(str(x) for x in dev_chan).encode())
|
||||
ans = SPIclient.recv().decode()
|
||||
if ans == 'ACK':
|
||||
TCP.send_msg(conn, 'I set voltage of output {:.0f} to {: f}'.format(tmp[2],tmp[3]))
|
||||
else:
|
||||
print('Error!')
|
||||
else:
|
||||
print('Not a command!')
|
||||
#reply = reply + ' '*(1024 - len(reply))
|
||||
#conn.send(reply.encode())
|
||||
busy = False
|
||||
|
||||
busy = False
|
||||
if not globalvars.stopall:
|
||||
print('close thread')
|
||||
else:
|
||||
print('close thread because of global stop')
|
||||
#reply = reply + ' '*(1024 - len(reply))
|
||||
#conn.sendall(reply.encode())
|
||||
conn.close()
|
||||
|
||||
#Function to convert byte string to int/float
|
||||
def str2num(s):
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
try:
|
||||
return float(s)
|
||||
except ValueError:
|
||||
return 0
|
||||
4
src/globalvars.py
Normal file
4
src/globalvars.py
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
stopall = False
|
||||
pidrun = [False, False, False, False]
|
||||
output_volt = [0,0,0,0]
|
||||
116
src/gui-client/channel.py
Normal file
116
src/gui-client/channel.py
Normal file
@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'channel.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.14.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_Form(object):
|
||||
def setupUi(self, Form):
|
||||
Form.setObjectName("Form")
|
||||
Form.resize(846, 423)
|
||||
self.gridLayout = QtWidgets.QGridLayout(Form)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
|
||||
self.gridLayout.addItem(spacerItem, 6, 1, 1, 1)
|
||||
self.label = QtWidgets.QLabel(Form)
|
||||
self.label.setObjectName("label")
|
||||
self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
|
||||
self.label_2 = QtWidgets.QLabel(Form)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
|
||||
spacerItem1 = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
|
||||
self.gridLayout.addItem(spacerItem1, 7, 1, 1, 1)
|
||||
self.PIDenableButton = QtWidgets.QPushButton(Form)
|
||||
self.PIDenableButton.setStyleSheet("background-color: rgb(255,0,0)")
|
||||
self.PIDenableButton.setCheckable(True)
|
||||
self.PIDenableButton.setObjectName("PIDenableButton")
|
||||
self.gridLayout.addWidget(self.PIDenableButton, 4, 0, 1, 1)
|
||||
self.setpointBox = QtWidgets.QDoubleSpinBox(Form)
|
||||
self.setpointBox.setDecimals(4)
|
||||
self.setpointBox.setSingleStep(0.01)
|
||||
self.setpointBox.setProperty("value", 22.0)
|
||||
self.setpointBox.setObjectName("setpointBox")
|
||||
self.gridLayout.addWidget(self.setpointBox, 0, 1, 1, 1)
|
||||
self.kdBox = QtWidgets.QDoubleSpinBox(Form)
|
||||
self.kdBox.setMinimum(-99.0)
|
||||
self.kdBox.setSingleStep(0.01)
|
||||
self.kdBox.setProperty("value", 1.0)
|
||||
self.kdBox.setObjectName("kdBox")
|
||||
self.gridLayout.addWidget(self.kdBox, 3, 1, 1, 1)
|
||||
self.outputBox = QtWidgets.QDoubleSpinBox(Form)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.outputBox.sizePolicy().hasHeightForWidth())
|
||||
self.outputBox.setSizePolicy(sizePolicy)
|
||||
self.outputBox.setMaximumSize(QtCore.QSize(100, 16777215))
|
||||
self.outputBox.setDecimals(3)
|
||||
self.outputBox.setSingleStep(0.01)
|
||||
self.outputBox.setObjectName("outputBox")
|
||||
self.gridLayout.addWidget(self.outputBox, 5, 0, 1, 1)
|
||||
self.label_4 = QtWidgets.QLabel(Form)
|
||||
self.label_4.setObjectName("label_4")
|
||||
self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1)
|
||||
self.kpBox = QtWidgets.QDoubleSpinBox(Form)
|
||||
self.kpBox.setMinimum(-99.0)
|
||||
self.kpBox.setSingleStep(0.01)
|
||||
self.kpBox.setProperty("value", 1.0)
|
||||
self.kpBox.setObjectName("kpBox")
|
||||
self.gridLayout.addWidget(self.kpBox, 1, 1, 1, 1)
|
||||
self.label_3 = QtWidgets.QLabel(Form)
|
||||
self.label_3.setObjectName("label_3")
|
||||
self.gridLayout.addWidget(self.label_3, 3, 0, 1, 1)
|
||||
self.OutputButton = QtWidgets.QPushButton(Form)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.OutputButton.sizePolicy().hasHeightForWidth())
|
||||
self.OutputButton.setSizePolicy(sizePolicy)
|
||||
self.OutputButton.setMaximumSize(QtCore.QSize(100, 16777215))
|
||||
self.OutputButton.setObjectName("OutputButton")
|
||||
self.gridLayout.addWidget(self.OutputButton, 5, 1, 1, 1)
|
||||
self.kiBox = QtWidgets.QDoubleSpinBox(Form)
|
||||
self.kiBox.setMinimum(-99.0)
|
||||
self.kiBox.setSingleStep(0.01)
|
||||
self.kiBox.setProperty("value", 1.0)
|
||||
self.kiBox.setObjectName("kiBox")
|
||||
self.gridLayout.addWidget(self.kiBox, 2, 1, 1, 1)
|
||||
self.widget = QtWidgets.QWidget(Form)
|
||||
self.widget.setObjectName("widget")
|
||||
self.gridLayout_2 = QtWidgets.QGridLayout(self.widget)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
self.widget_2 = QtWidgets.QWidget(self.widget)
|
||||
self.widget_2.setObjectName("widget_2")
|
||||
self.temp_layout = QtWidgets.QHBoxLayout(self.widget_2)
|
||||
self.temp_layout.setObjectName("temp_layout")
|
||||
self.gridLayout_2.addWidget(self.widget_2, 0, 0, 1, 1)
|
||||
self.widget_3 = QtWidgets.QWidget(self.widget)
|
||||
self.widget_3.setObjectName("widget_3")
|
||||
self.error_layout = QtWidgets.QHBoxLayout(self.widget_3)
|
||||
self.error_layout.setObjectName("error_layout")
|
||||
self.gridLayout_2.addWidget(self.widget_3, 0, 1, 1, 1)
|
||||
self.widget_4 = QtWidgets.QWidget(self.widget)
|
||||
self.widget_4.setObjectName("widget_4")
|
||||
self.output_layout = QtWidgets.QHBoxLayout(self.widget_4)
|
||||
self.output_layout.setObjectName("output_layout")
|
||||
self.gridLayout_2.addWidget(self.widget_4, 1, 0, 1, 2)
|
||||
self.gridLayout.addWidget(self.widget, 0, 2, 8, 1)
|
||||
|
||||
self.retranslateUi(Form)
|
||||
QtCore.QMetaObject.connectSlotsByName(Form)
|
||||
|
||||
def retranslateUi(self, Form):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
Form.setWindowTitle(_translate("Form", "Form"))
|
||||
self.label.setText(_translate("Form", "kp"))
|
||||
self.label_2.setText(_translate("Form", "ki"))
|
||||
self.PIDenableButton.setText(_translate("Form", "PID enable"))
|
||||
self.label_4.setText(_translate("Form", "Setpoint (°C)"))
|
||||
self.label_3.setText(_translate("Form", "kd"))
|
||||
self.OutputButton.setText(_translate("Form", "Set output"))
|
||||
208
src/gui-client/channel.ui
Normal file
208
src/gui-client/channel.ui
Normal file
@ -0,0 +1,208 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>846</width>
|
||||
<height>423</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="6" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Minimum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>kp</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>ki</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Preferred</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QPushButton" name="PIDenableButton">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(255,0,0)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PID enable</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="setpointBox">
|
||||
<property name="decimals">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>22.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QDoubleSpinBox" name="kdBox">
|
||||
<property name="minimum">
|
||||
<double>-99.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QDoubleSpinBox" name="outputBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Setpoint (°C)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QDoubleSpinBox" name="kpBox">
|
||||
<property name="minimum">
|
||||
<double>-99.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>kd</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QPushButton" name="OutputButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>100</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set output</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QDoubleSpinBox" name="kiBox">
|
||||
<property name="minimum">
|
||||
<double>-99.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.010000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="8">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QWidget" name="widget_2" native="true">
|
||||
<layout class="QHBoxLayout" name="temp_layout"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QWidget" name="widget_3" native="true">
|
||||
<layout class="QHBoxLayout" name="error_layout"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QWidget" name="widget_4" native="true">
|
||||
<layout class="QHBoxLayout" name="output_layout"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
170
src/gui-client/channelInfoClass.py
Normal file
170
src/gui-client/channelInfoClass.py
Normal file
@ -0,0 +1,170 @@
|
||||
from PyQt5.QtCore import QObject
|
||||
|
||||
import numpy as np
|
||||
import ast
|
||||
import socket
|
||||
|
||||
import tcptools as TCP
|
||||
import globalvars
|
||||
|
||||
from channelInfoWidgetClass import channelInfoWidget
|
||||
|
||||
class channelInfo(QObject):
|
||||
def __init__(self, channel_num, temp_field, out_field, enable_field, tabnumber, activTabWidget, parent):
|
||||
super(channelInfo,self).__init__(parent)
|
||||
self.widget_obj = channelInfoWidget(self)
|
||||
self.channel_num = channel_num
|
||||
|
||||
self.temp_field = temp_field
|
||||
self.out_field = out_field
|
||||
self.enable_field = enable_field
|
||||
|
||||
self.tabNumber = tabnumber
|
||||
self.activTabWidget = activTabWidget
|
||||
|
||||
self.data_length = 64
|
||||
self.temp_data = np.array([21.0] * self.data_length)
|
||||
self.error_data = np.array([0.0] * self.data_length)
|
||||
self.output_data = np.array([0.0] * self.data_length)
|
||||
self.interror_data = np.array([0.0] * self.data_length)
|
||||
|
||||
self.temp_plot = self.widget_obj.temp_plot.plot(y=self.temp_data,clickable=True)
|
||||
self.output_plot = self.widget_obj.output_plot.plot(y=self.output_data,clickable=True)
|
||||
self.error_plot = self.widget_obj.error_plot.plot(y=self.error_data,clickable=True)
|
||||
|
||||
self.widget_obj.setpointBox.valueChanged.connect(self.setpointChanged)
|
||||
self.widget_obj.kpBox.valueChanged.connect(self.kpChanged)
|
||||
self.widget_obj.kiBox.valueChanged.connect(self.kiChanged)
|
||||
self.widget_obj.kdBox.valueChanged.connect(self.kdChanged)
|
||||
self.widget_obj.PIDenableButton.clicked.connect(self.PIDenableButtonChanged)
|
||||
self.widget_obj.OutputButton.clicked.connect(self.OutputButtonChanged)
|
||||
|
||||
|
||||
def update(self):
|
||||
if globalvars.conStatus:
|
||||
TCP.send_cmd(globalvars.sock, 'CMD:\tGET_RUN_VALUES\t'+str(self.channel_num)+'\t')
|
||||
msg = TCP.recv_msg(globalvars.sock)
|
||||
|
||||
#ans = str2num(msg)
|
||||
try:
|
||||
ans = ast.literal_eval(msg)
|
||||
except ValueError:
|
||||
ans = [100, 0, 0]
|
||||
|
||||
|
||||
self.temp_data = np.roll(self.temp_data, -1)
|
||||
self.temp_data[self.data_length-1] = ans[0]
|
||||
self.temp_field.setText("{:.4f}".format(ans[0]))
|
||||
|
||||
self.error_data = np.roll(self.error_data, -1)
|
||||
self.error_data[self.data_length-1] = ans[1]
|
||||
|
||||
self.output_data = np.roll(self.output_data, -1)
|
||||
self.output_data[self.data_length-1] = ans[2]
|
||||
self.out_field.setText("{:.4f}".format(ans[2]))
|
||||
|
||||
if self.tabNumber == self.activTabWidget.currentIndex():
|
||||
self.temp_plot.setData(y=self.temp_data)
|
||||
self.error_plot.setData(y=self.error_data)
|
||||
self.output_plot.setData(y=self.output_data)
|
||||
|
||||
|
||||
def setpointChanged(self):
|
||||
if globalvars.conStatus:
|
||||
TCP.send_cmd(globalvars.sock, 'CMD:\tSET_SETPOINT\t'+str(self.channel_num)+'\t'+str(self.widget_obj.setpointBox.value())+'\t')
|
||||
ans = TCP.recv_msg(globalvars.sock)
|
||||
print(ans)
|
||||
globalvars.settings["ch{0}".format(self.channel_num)]["setpoint"] = self.widget_obj.setpointBox.value()
|
||||
|
||||
def kpChanged(self):
|
||||
if globalvars.conStatus:
|
||||
TCP.send_cmd(globalvars.sock, 'CMD:\tSET_KP\t'+str(self.channel_num)+'\t'+str(self.widget_obj.kpBox.value())+'\t')
|
||||
ans = TCP.recv_msg(globalvars.sock)
|
||||
print(ans)
|
||||
|
||||
def kiChanged(self):
|
||||
if globalvars.conStatus:
|
||||
TCP.send_cmd(globalvars.sock, 'CMD:\tSET_KI\t'+str(self.channel_num)+'\t'+str(self.widget_obj.kiBox.value())+'\t')
|
||||
ans = TCP.recv_msg(globalvars.sock)
|
||||
print(ans)
|
||||
|
||||
def kdChanged(self):
|
||||
if globalvars.conStatus:
|
||||
TCP.send_cmd(globalvars.sock, 'CMD:\tSET_KD\t'+str(self.channel_num)+'\t'+str(self.widget_obj.kdBox.value())+'\t')
|
||||
ans = TCP.recv_msg(globalvars.sock)
|
||||
print(ans)
|
||||
|
||||
def PIDenableButtonChanged(self):
|
||||
if globalvars.conStatus:
|
||||
if self.widget_obj.PIDenableButton.isChecked():
|
||||
self.widget_obj.PIDenableButton.setStyleSheet("background-color: rgb(0,255,0)")
|
||||
self.widget_obj.OutputButton.setEnabled(False)
|
||||
TCP.send_cmd(globalvars.sock, 'CMD:\tLOCK\t'+str(self.channel_num)+'\t')
|
||||
ans = TCP.recv_msg(globalvars.sock)
|
||||
print(ans)
|
||||
self.enable_field.setStyleSheet("background-color: rgb(0,255,0)")
|
||||
else:
|
||||
self.widget_obj.PIDenableButton.setStyleSheet("background-color: rgb(255,0,0)")
|
||||
self.widget_obj.OutputButton.setEnabled(True)
|
||||
TCP.send_cmd(globalvars.sock, 'CMD:\tIDLE\t'+str(self.channel_num)+'\t')
|
||||
ans = TCP.recv_msg(globalvars.sock)
|
||||
print(ans)
|
||||
self.enable_field.setStyleSheet("background-color: rgb(255,0,0)")
|
||||
else:
|
||||
self.widget_obj.PIDenableButton.setChecked(False)
|
||||
|
||||
def OutputButtonChanged(self):
|
||||
if globalvars.conStatus:
|
||||
TCP.send_cmd(globalvars.sock, 'CMD:\tSET_VOLTAGE\t'+str(self.channel_num)+'\t'+str(self.widget_obj.outputBox.value())+'\t')
|
||||
ans = TCP.recv_msg(globalvars.sock)
|
||||
print(ans)
|
||||
|
||||
def setEnabled(self, val):
|
||||
self.widget_obj.setEnabled(val)
|
||||
|
||||
def loadSettings(self):
|
||||
if globalvars.remoteSettings:
|
||||
TCP.send_cmd(globalvars.sock,"CMD:\tGET_SETPOINT\t{0}\t".format(self.channel_num))
|
||||
setpoint = float(TCP.recv_msg(globalvars.sock))
|
||||
self.widget_obj.setpointBox.blockSignals(True)
|
||||
self.widget_obj.setpointBox.setValue(setpoint)
|
||||
self.widget_obj.setpointBox.blockSignals(False)
|
||||
|
||||
TCP.send_cmd(globalvars.sock,"CMD:\tGET_KP\t{0}\t".format(self.channel_num))
|
||||
kp = float(TCP.recv_msg(globalvars.sock))
|
||||
self.widget_obj.kpBox.blockSignals(True)
|
||||
self.widget_obj.kpBox.setValue(kp)
|
||||
self.widget_obj.kpBox.blockSignals(False)
|
||||
|
||||
TCP.send_cmd(globalvars.sock,"CMD:\tGET_KI\t{0}\t".format(self.channel_num))
|
||||
ki = float(TCP.recv_msg(globalvars.sock))
|
||||
self.widget_obj.kiBox.blockSignals(True)
|
||||
self.widget_obj.kiBox.setValue(ki)
|
||||
self.widget_obj.kiBox.blockSignals(False)
|
||||
|
||||
TCP.send_cmd(globalvars.sock,"CMD:\tGET_KD\t{0}\t".format(self.channel_num))
|
||||
kd = float(TCP.recv_msg(globalvars.sock))
|
||||
self.widget_obj.kdBox.blockSignals(True)
|
||||
self.widget_obj.kdBox.setValue(kd)
|
||||
self.widget_obj.kdBox.blockSignals(False)
|
||||
|
||||
TCP.send_cmd(globalvars.sock,"CMD:\tGET_PID_STATUS\t{0}\t".format(self.channel_num))
|
||||
enabled = int(TCP.recv_msg(globalvars.sock))
|
||||
print(enabled)
|
||||
self.widget_obj.PIDenableButton.setChecked(enabled)
|
||||
if self.widget_obj.PIDenableButton.isChecked():
|
||||
self.widget_obj.PIDenableButton.setStyleSheet("background-color: rgb(0,255,0)")
|
||||
self.widget_obj.OutputButton.setEnabled(False)
|
||||
self.enable_field.setStyleSheet("background-color: rgb(0,255,0)")
|
||||
else:
|
||||
self.widget_obj.PIDenableButton.setStyleSheet("background-color: rgb(255,0,0)")
|
||||
self.widget_obj.OutputButton.setEnabled(True)
|
||||
self.enable_field.setStyleSheet("background-color: rgb(255,0,0)")
|
||||
|
||||
else:
|
||||
#Set settings from file and trigger sending to server
|
||||
self.widget_obj.setpointBox.setValue(float(globalvars.settings["ch{0}".format(self.channel_num)]["setpoint"]))
|
||||
self.widget_obj.kpBox.setValue(float(globalvars.settings["ch{0}".format(self.channel_num)]["kp"]))
|
||||
self.widget_obj.kiBox.setValue(float(globalvars.settings["ch{0}".format(self.channel_num)]["ki"]))
|
||||
self.widget_obj.kdBox.setValue(float(globalvars.settings["ch{0}".format(self.channel_num)]["kd"]))
|
||||
self.widget_obj.PIDenableButton.setChecked(bool(globalvars.settings["ch{0}".format(self.channel_num)]["PIDenabled"]))
|
||||
24
src/gui-client/channelInfoWidgetClass.py
Normal file
24
src/gui-client/channelInfoWidgetClass.py
Normal file
@ -0,0 +1,24 @@
|
||||
from PyQt5.QtWidgets import QWidget
|
||||
import pyqtgraph as pg
|
||||
|
||||
from channel import Ui_Form
|
||||
|
||||
class channelInfoWidget(QWidget, Ui_Form):
|
||||
def __init__(self, parent):
|
||||
QWidget.__init__(self)
|
||||
self.setupUi(self)
|
||||
|
||||
self.temp_plot = pg.PlotWidget()
|
||||
self.temp_layout.addWidget(self.temp_plot)
|
||||
self.temp_plot.setLabel('left', "temp", units='°C')
|
||||
self.temp_plot.setLabel('bottom', "time")#, units='V')
|
||||
|
||||
self.error_plot = pg.PlotWidget()
|
||||
self.error_layout.addWidget(self.error_plot)
|
||||
self.error_plot.setLabel('left', "error", units='°C')
|
||||
self.error_plot.setLabel('bottom', "time")#, units='V')
|
||||
|
||||
self.output_plot = pg.PlotWidget()
|
||||
self.output_layout.addWidget(self.output_plot)
|
||||
self.output_plot.setLabel('left', "U", units='V')
|
||||
self.output_plot.setLabel('bottom', "time")#, units='V')
|
||||
170
src/gui-client/client.py
Normal file
170
src/gui-client/client.py
Normal file
@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Jan 15 14:30:51 2018
|
||||
|
||||
@author: jan
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtWidgets import QApplication, QMainWindow
|
||||
from PyQt5.QtCore import QTimer
|
||||
|
||||
import socket
|
||||
import os
|
||||
import json
|
||||
|
||||
import queue
|
||||
|
||||
from main_window import Ui_MainWindow
|
||||
|
||||
from channelInfoClass import channelInfo
|
||||
|
||||
import globalvars
|
||||
######################################################################
|
||||
#TCP/IP
|
||||
HOST = '' #Symbolic name
|
||||
PORT = 2000
|
||||
|
||||
######################################################################
|
||||
|
||||
#Function to convert byte string to int/float
|
||||
def str2num(s):
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
try:
|
||||
return float(s)
|
||||
except ValueError:
|
||||
return 0
|
||||
|
||||
class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
def __init__(self, ):
|
||||
QMainWindow.__init__(self)
|
||||
self.setupUi(self)
|
||||
|
||||
self.tcpqueue = queue.Queue(100)
|
||||
|
||||
self.channel = [0]
|
||||
|
||||
temp_field = [self.temp_ch0]
|
||||
out_field = [self.out_ch0]
|
||||
enable_field = [self.enable_ch0]
|
||||
|
||||
#Open json settings file defined in globalvars
|
||||
self.loadSettingsFile(globalvars.settingsFile)
|
||||
|
||||
for ch in range(len(self.channel)):
|
||||
self.channel[ch] = channelInfo(ch,temp_field[ch],out_field[ch],enable_field[ch], ch, self.tabWidget ,self)
|
||||
self.channel[ch].setEnabled(False)
|
||||
|
||||
self.layout_ch0.addWidget(self.channel[0].widget_obj)
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_0),globalvars.settings["ch0"]["name"])
|
||||
#self.layout_ch1.addWidget(self.channel[1].widget_obj)
|
||||
#self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_1),globalvars.settings["ch1"]["name"])
|
||||
#self.layout_ch2.addWidget(self.channel[2].widget_obj)
|
||||
#self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2),globalvars.settings["ch2"]["name"])
|
||||
#self.layout_ch3.addWidget(self.channel[3].widget_obj)
|
||||
#self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3),globalvars.settings["ch3"]["name"])
|
||||
|
||||
self.ip_address.setText(globalvars.settings["defaultIP"])
|
||||
self.portField.setText(str(globalvars.settings["defaultPort"]))
|
||||
|
||||
#Connect radio buttons to functions
|
||||
self.remoteRadioButton.toggled.connect(lambda:self.settingsSelection(self.remoteRadioButton))
|
||||
self.localRadioButton.toggled.connect(lambda:self.settingsSelection(self.localRadioButton))
|
||||
|
||||
# Close event to save settings
|
||||
app.aboutToQuit.connect(self.closeEvent)
|
||||
# Button functions
|
||||
self.connectButton.clicked.connect(self.connectButtonChange)
|
||||
|
||||
self.refresh_timer = QTimer()
|
||||
self.refresh_timer.timeout.connect(self.refresh)
|
||||
|
||||
def connectButtonChange(self):
|
||||
if self.connectButton.isChecked():
|
||||
#Open connection to server
|
||||
self.sock = socket.socket()
|
||||
self.address = self.ip_address.text()
|
||||
self.port = self.portField.text()
|
||||
|
||||
try:
|
||||
self.sock.connect((self.address,int(self.port)))
|
||||
except Exception as e:
|
||||
print("something's wrong with %s:%s. Exception is %s" % (self.address, self.port, e))
|
||||
globalvars.conStatus = False
|
||||
self.connectButton.setChecked(False)
|
||||
return
|
||||
|
||||
try:
|
||||
data_recv = self.sock.recv(1024)
|
||||
print(data_recv.decode())
|
||||
except OSError:
|
||||
globalvars.conStatus = False
|
||||
self.connectButton.setChecked(False)
|
||||
print('\nSocket broken')
|
||||
return
|
||||
|
||||
globalvars.conStatus = True
|
||||
globalvars.sock = self.sock
|
||||
print("connect to "+ self.address)
|
||||
|
||||
for ch in range(len(self.channel)):
|
||||
self.channel[ch].loadSettings()
|
||||
|
||||
self.refresh_timer.start(500)
|
||||
|
||||
else:
|
||||
globalvars.conStatus = False
|
||||
self.refresh_timer.stop()
|
||||
time.sleep(0.1)
|
||||
print("disconnect from "+ self.address)
|
||||
self.sock.close()
|
||||
|
||||
for ch in range(len(self.channel)):
|
||||
self.channel[ch].setEnabled(globalvars.conStatus)
|
||||
|
||||
self.remoteRadioButton.setEnabled(not globalvars.conStatus)
|
||||
self.localRadioButton.setEnabled(not globalvars.conStatus)
|
||||
|
||||
def refresh(self):
|
||||
for ch in range(len(self.channel)):
|
||||
pass
|
||||
self.channel[ch].update()
|
||||
|
||||
def settingsSelection(self,b):
|
||||
if b.objectName() == "remoteRadioButton":
|
||||
if b.isChecked() == True:
|
||||
globalvars.remoteSettings = True
|
||||
else:
|
||||
globalvars.remoteSettings = False
|
||||
|
||||
if b.objectName() == "localRadioButton":
|
||||
if b.isChecked() == True:
|
||||
globalvars.remoteSettings = False
|
||||
else:
|
||||
globalvars.remoteSettings = True
|
||||
|
||||
def loadSettingsFile(self,path):
|
||||
with open(path) as c:
|
||||
globalvars.settings = json.load(c)
|
||||
|
||||
def closeEvent(self, event):
|
||||
globalvars.conStatus = False
|
||||
|
||||
print("Saving settings... ")
|
||||
with open(globalvars.settingsFile, 'w') as f:
|
||||
json.dump(globalvars.settings, f, ensure_ascii=True)
|
||||
print('done.')
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
main = ApplicationWindow()
|
||||
main.setWindowTitle("Tempertur-Controller V1")
|
||||
main.show()
|
||||
app.exec_()
|
||||
6
src/gui-client/globalvars.py
Normal file
6
src/gui-client/globalvars.py
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
conStatus = False
|
||||
sock = 0
|
||||
settings = 0
|
||||
remoteSettings = True
|
||||
settingsFile = './settings.json'
|
||||
187
src/gui-client/main_window.py
Normal file
187
src/gui-client/main_window.py
Normal file
@ -0,0 +1,187 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Form implementation generated from reading ui file 'main_window.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.14.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
MainWindow.setObjectName("MainWindow")
|
||||
MainWindow.resize(881, 742)
|
||||
self.centralwidget = QtWidgets.QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName("centralwidget")
|
||||
self.gridLayout_2 = QtWidgets.QGridLayout(self.centralwidget)
|
||||
self.gridLayout_2.setObjectName("gridLayout_2")
|
||||
self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth())
|
||||
self.groupBox.setSizePolicy(sizePolicy)
|
||||
self.groupBox.setMinimumSize(QtCore.QSize(220, 0))
|
||||
self.groupBox.setMaximumSize(QtCore.QSize(157, 16777215))
|
||||
self.groupBox.setObjectName("groupBox")
|
||||
self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox)
|
||||
self.gridLayout_3.setObjectName("gridLayout_3")
|
||||
self.label_5 = QtWidgets.QLabel(self.groupBox)
|
||||
self.label_5.setObjectName("label_5")
|
||||
self.gridLayout_3.addWidget(self.label_5, 0, 1, 1, 1)
|
||||
self.connectButton = QtWidgets.QPushButton(self.groupBox)
|
||||
self.connectButton.setCheckable(True)
|
||||
self.connectButton.setObjectName("connectButton")
|
||||
self.gridLayout_3.addWidget(self.connectButton, 5, 0, 1, 3)
|
||||
self.localRadioButton = QtWidgets.QRadioButton(self.groupBox)
|
||||
self.localRadioButton.setObjectName("localRadioButton")
|
||||
self.gridLayout_3.addWidget(self.localRadioButton, 4, 0, 1, 3)
|
||||
self.remoteRadioButton = QtWidgets.QRadioButton(self.groupBox)
|
||||
self.remoteRadioButton.setChecked(True)
|
||||
self.remoteRadioButton.setObjectName("remoteRadioButton")
|
||||
self.gridLayout_3.addWidget(self.remoteRadioButton, 3, 0, 1, 3)
|
||||
self.label = QtWidgets.QLabel(self.groupBox)
|
||||
self.label.setObjectName("label")
|
||||
self.gridLayout_3.addWidget(self.label, 0, 0, 1, 1)
|
||||
self.ip_address = QtWidgets.QLineEdit(self.groupBox)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.ip_address.sizePolicy().hasHeightForWidth())
|
||||
self.ip_address.setSizePolicy(sizePolicy)
|
||||
self.ip_address.setMaximumSize(QtCore.QSize(16777215, 16777215))
|
||||
self.ip_address.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.ip_address.setObjectName("ip_address")
|
||||
self.gridLayout_3.addWidget(self.ip_address, 1, 0, 1, 1)
|
||||
self.portField = QtWidgets.QLineEdit(self.groupBox)
|
||||
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(self.portField.sizePolicy().hasHeightForWidth())
|
||||
self.portField.setSizePolicy(sizePolicy)
|
||||
self.portField.setMaximumSize(QtCore.QSize(50, 16777215))
|
||||
self.portField.setObjectName("portField")
|
||||
self.gridLayout_3.addWidget(self.portField, 1, 1, 1, 2)
|
||||
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout_3.addItem(spacerItem, 6, 0, 1, 1)
|
||||
self.gridLayout_2.addWidget(self.groupBox, 0, 0, 1, 1)
|
||||
self.groupBox_2 = QtWidgets.QGroupBox(self.centralwidget)
|
||||
self.groupBox_2.setObjectName("groupBox_2")
|
||||
self.gridLayout_4 = QtWidgets.QGridLayout(self.groupBox_2)
|
||||
self.gridLayout_4.setObjectName("gridLayout_4")
|
||||
self.label_2 = QtWidgets.QLabel(self.groupBox_2)
|
||||
self.label_2.setObjectName("label_2")
|
||||
self.gridLayout_4.addWidget(self.label_2, 0, 0, 1, 1)
|
||||
self.out_ch1 = QtWidgets.QLineEdit(self.groupBox_2)
|
||||
self.out_ch1.setObjectName("out_ch1")
|
||||
self.gridLayout_4.addWidget(self.out_ch1, 2, 1, 1, 1)
|
||||
self.label_3 = QtWidgets.QLabel(self.groupBox_2)
|
||||
self.label_3.setObjectName("label_3")
|
||||
self.gridLayout_4.addWidget(self.label_3, 0, 2, 1, 1)
|
||||
self.temp_ch2 = QtWidgets.QLineEdit(self.groupBox_2)
|
||||
self.temp_ch2.setObjectName("temp_ch2")
|
||||
self.gridLayout_4.addWidget(self.temp_ch2, 3, 0, 1, 1)
|
||||
self.out_ch2 = QtWidgets.QLineEdit(self.groupBox_2)
|
||||
self.out_ch2.setObjectName("out_ch2")
|
||||
self.gridLayout_4.addWidget(self.out_ch2, 3, 1, 1, 1)
|
||||
self.out_ch3 = QtWidgets.QLineEdit(self.groupBox_2)
|
||||
self.out_ch3.setObjectName("out_ch3")
|
||||
self.gridLayout_4.addWidget(self.out_ch3, 4, 1, 1, 1)
|
||||
self.temp_ch0 = QtWidgets.QLineEdit(self.groupBox_2)
|
||||
self.temp_ch0.setObjectName("temp_ch0")
|
||||
self.gridLayout_4.addWidget(self.temp_ch0, 1, 0, 1, 1)
|
||||
self.enable_ch3 = QtWidgets.QPushButton(self.groupBox_2)
|
||||
self.enable_ch3.setStyleSheet("background-color: rgb(255,0,0)")
|
||||
self.enable_ch3.setText("")
|
||||
self.enable_ch3.setObjectName("enable_ch3")
|
||||
self.gridLayout_4.addWidget(self.enable_ch3, 4, 2, 1, 1)
|
||||
self.temp_ch1 = QtWidgets.QLineEdit(self.groupBox_2)
|
||||
self.temp_ch1.setObjectName("temp_ch1")
|
||||
self.gridLayout_4.addWidget(self.temp_ch1, 2, 0, 1, 1)
|
||||
self.enable_ch2 = QtWidgets.QPushButton(self.groupBox_2)
|
||||
self.enable_ch2.setStyleSheet("background-color: rgb(255,0,0)")
|
||||
self.enable_ch2.setText("")
|
||||
self.enable_ch2.setObjectName("enable_ch2")
|
||||
self.gridLayout_4.addWidget(self.enable_ch2, 3, 2, 1, 1)
|
||||
self.temp_ch3 = QtWidgets.QLineEdit(self.groupBox_2)
|
||||
self.temp_ch3.setObjectName("temp_ch3")
|
||||
self.gridLayout_4.addWidget(self.temp_ch3, 4, 0, 1, 1)
|
||||
self.label_4 = QtWidgets.QLabel(self.groupBox_2)
|
||||
self.label_4.setObjectName("label_4")
|
||||
self.gridLayout_4.addWidget(self.label_4, 0, 1, 1, 1)
|
||||
self.out_ch0 = QtWidgets.QLineEdit(self.groupBox_2)
|
||||
self.out_ch0.setObjectName("out_ch0")
|
||||
self.gridLayout_4.addWidget(self.out_ch0, 1, 1, 1, 1)
|
||||
self.enable_ch0 = QtWidgets.QPushButton(self.groupBox_2)
|
||||
self.enable_ch0.setStyleSheet("background-color: rgb(255,0,0)")
|
||||
self.enable_ch0.setText("")
|
||||
self.enable_ch0.setCheckable(False)
|
||||
self.enable_ch0.setChecked(False)
|
||||
self.enable_ch0.setObjectName("enable_ch0")
|
||||
self.gridLayout_4.addWidget(self.enable_ch0, 1, 2, 1, 1)
|
||||
self.enable_ch1 = QtWidgets.QPushButton(self.groupBox_2)
|
||||
self.enable_ch1.setStyleSheet("background-color: rgb(255,0,0)")
|
||||
self.enable_ch1.setText("")
|
||||
self.enable_ch1.setObjectName("enable_ch1")
|
||||
self.gridLayout_4.addWidget(self.enable_ch1, 2, 2, 1, 1)
|
||||
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
self.gridLayout_4.addItem(spacerItem1, 5, 0, 1, 1)
|
||||
self.gridLayout_2.addWidget(self.groupBox_2, 0, 1, 1, 1)
|
||||
self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
|
||||
self.tabWidget.setObjectName("tabWidget")
|
||||
self.tab_0 = QtWidgets.QWidget()
|
||||
self.tab_0.setObjectName("tab_0")
|
||||
self.layout_ch0 = QtWidgets.QHBoxLayout(self.tab_0)
|
||||
self.layout_ch0.setObjectName("layout_ch0")
|
||||
self.tabWidget.addTab(self.tab_0, "")
|
||||
self.tab_1 = QtWidgets.QWidget()
|
||||
self.tab_1.setObjectName("tab_1")
|
||||
self.layout_ch1 = QtWidgets.QHBoxLayout(self.tab_1)
|
||||
self.layout_ch1.setObjectName("layout_ch1")
|
||||
self.tabWidget.addTab(self.tab_1, "")
|
||||
self.tab_2 = QtWidgets.QWidget()
|
||||
self.tab_2.setObjectName("tab_2")
|
||||
self.layout_ch2 = QtWidgets.QHBoxLayout(self.tab_2)
|
||||
self.layout_ch2.setObjectName("layout_ch2")
|
||||
self.tabWidget.addTab(self.tab_2, "")
|
||||
self.tab_3 = QtWidgets.QWidget()
|
||||
self.tab_3.setObjectName("tab_3")
|
||||
self.layout_ch3 = QtWidgets.QHBoxLayout(self.tab_3)
|
||||
self.layout_ch3.setObjectName("layout_ch3")
|
||||
self.tabWidget.addTab(self.tab_3, "")
|
||||
self.gridLayout_2.addWidget(self.tabWidget, 1, 0, 1, 2)
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 881, 22))
|
||||
self.menubar.setObjectName("menubar")
|
||||
MainWindow.setMenuBar(self.menubar)
|
||||
self.statusbar = QtWidgets.QStatusBar(MainWindow)
|
||||
self.statusbar.setObjectName("statusbar")
|
||||
MainWindow.setStatusBar(self.statusbar)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
_translate = QtCore.QCoreApplication.translate
|
||||
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
|
||||
self.groupBox.setTitle(_translate("MainWindow", "Connection setup"))
|
||||
self.label_5.setText(_translate("MainWindow", "Port:"))
|
||||
self.connectButton.setText(_translate("MainWindow", "Connect"))
|
||||
self.localRadioButton.setText(_translate("MainWindow", "use local settings"))
|
||||
self.remoteRadioButton.setText(_translate("MainWindow", "use remote settings"))
|
||||
self.label.setText(_translate("MainWindow", "IP:"))
|
||||
self.ip_address.setText(_translate("MainWindow", "134.96.13.239"))
|
||||
self.portField.setText(_translate("MainWindow", "2000"))
|
||||
self.groupBox_2.setTitle(_translate("MainWindow", "Overview"))
|
||||
self.label_2.setText(_translate("MainWindow", "temperatures (°C)"))
|
||||
self.label_3.setText(_translate("MainWindow", "PID enabled"))
|
||||
self.label_4.setText(_translate("MainWindow", "outputs (V)"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_0), _translate("MainWindow", "Channel 0"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_1), _translate("MainWindow", "Channel 1"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Channel 2"))
|
||||
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Channel 3"))
|
||||
300
src/gui-client/main_window.ui
Normal file
300
src/gui-client/main_window.ui
Normal file
@ -0,0 +1,300 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>881</width>
|
||||
<height>742</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>220</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>157</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Connection setup</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Port:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="3">
|
||||
<widget class="QPushButton" name="connectButton">
|
||||
<property name="text">
|
||||
<string>Connect</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="3">
|
||||
<widget class="QRadioButton" name="localRadioButton">
|
||||
<property name="text">
|
||||
<string>use local settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QRadioButton" name="remoteRadioButton">
|
||||
<property name="text">
|
||||
<string>use remote settings</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>IP:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLineEdit" name="ip_address">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>134.96.13.239</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="portField">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>2000</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Overview</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>temperatures (°C)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="out_ch1"/>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>PID enabled</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLineEdit" name="temp_ch2"/>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="out_ch2"/>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="out_ch3"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLineEdit" name="temp_ch0"/>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QPushButton" name="enable_ch3">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(255,0,0)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLineEdit" name="temp_ch1"/>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QPushButton" name="enable_ch2">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(255,0,0)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLineEdit" name="temp_ch3"/>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>outputs (V)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="out_ch0"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="enable_ch0">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(255,0,0)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QPushButton" name="enable_ch1">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(255,0,0)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab_0">
|
||||
<attribute name="title">
|
||||
<string>Channel 0</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="layout_ch0"/>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_1">
|
||||
<attribute name="title">
|
||||
<string>Channel 1</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="layout_ch1"/>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Channel 2</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="layout_ch2"/>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="title">
|
||||
<string>Channel 3</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="layout_ch3"/>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>881</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
129
src/gui-client/pin.py
Normal file
129
src/gui-client/pin.py
Normal file
@ -0,0 +1,129 @@
|
||||
from random import random
|
||||
import json
|
||||
|
||||
class InputOutputError(Exception):
|
||||
pass
|
||||
|
||||
conf={}
|
||||
TEST='test'
|
||||
conf[TEST]=False
|
||||
IN=1
|
||||
OUT=0
|
||||
HIGH=1
|
||||
LOW=0
|
||||
BCM=0
|
||||
BOARD=1
|
||||
pins={}
|
||||
out={}
|
||||
values={}
|
||||
|
||||
def config(path):
|
||||
global conf
|
||||
with open(path) as c:
|
||||
conf = json.load(c)
|
||||
if not conf[TEST]:
|
||||
global RPi
|
||||
global GPIO
|
||||
import RPi.GPIO as GPIO
|
||||
|
||||
#TODO check if initial is discarded for input or error is raised
|
||||
def setup(channel,in_out,initial=HIGH):
|
||||
global conf
|
||||
if type(channel) is list:
|
||||
for el in channel:
|
||||
_setup_one(el,in_out,initial)
|
||||
else:
|
||||
_setup_one(channel,in_out,initial)
|
||||
|
||||
def _setup_one(channel,in_out,initial):
|
||||
if conf[TEST]:
|
||||
pins[channel]=in_out
|
||||
else:
|
||||
if initial == HIGH:
|
||||
initial = GPIO.HIGH
|
||||
else:
|
||||
initial = GPIO.LOW
|
||||
if in_out == IN:
|
||||
GPIO.setup(channel,GPIO.IN) # initial not a valid parameter for input, GPIO error
|
||||
else:
|
||||
GPIO.setup(channel,GPIO.OUT,initial=initial)
|
||||
|
||||
def check_in_out(channel,in_out):
|
||||
try:
|
||||
if not pins[channel]==in_out:
|
||||
raise InputOutputError("Wrong confuration for channel {}! You're treating an input channel as output or vice versa.")
|
||||
except KeyError:
|
||||
raise InputOutputError("Wrong confuration for channel {}! setup() not called for this channel before calling input() or output().")
|
||||
|
||||
def setmode(mode):
|
||||
if conf[TEST]:
|
||||
pass
|
||||
else:
|
||||
if mode == BCM:
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
else:
|
||||
GPIO.setmode(GPIO.BOARD)
|
||||
|
||||
def input(channel):
|
||||
if conf[TEST]:
|
||||
check_in_out(channel,IN)
|
||||
try:
|
||||
values[channel]
|
||||
except KeyError:
|
||||
return random()
|
||||
return values[channel]
|
||||
else:
|
||||
return GPIO.input(channel)
|
||||
|
||||
def output(channel,value):
|
||||
if type(channel) is list:
|
||||
for el in channel:
|
||||
_output_one(el,value)
|
||||
else:
|
||||
_output_one(channel,value)
|
||||
|
||||
def _output_one(channel,value):
|
||||
if conf[TEST]:
|
||||
check_in_out(channel,OUT)
|
||||
out[channel]=value
|
||||
else:
|
||||
GPIO.output(channel,value)
|
||||
|
||||
def set_value(channel,value):
|
||||
values[channel]=value
|
||||
|
||||
def get_output(channel):
|
||||
return out[channel]
|
||||
|
||||
def cleanup(channel=None):
|
||||
global pins,out,values
|
||||
if channel==None:
|
||||
if conf[TEST]:
|
||||
pins,out,values={},{},{}
|
||||
else:
|
||||
GPIO.cleanup()
|
||||
else:
|
||||
if type(channel) is list or type(channel) is tuple:
|
||||
for el in channel:
|
||||
_cleanup_one(el)
|
||||
else:
|
||||
_cleanup_one(channel)
|
||||
|
||||
def _cleanup_one(channel):
|
||||
global pins,out,values
|
||||
if conf[TEST]:
|
||||
if channel in values.keys() and pins[channel]==IN:
|
||||
del values[channel]
|
||||
elif channel in out.keys():
|
||||
del out[channel]
|
||||
if channel in pins.keys():
|
||||
del pins[channel]
|
||||
else:
|
||||
GPIO.cleanup(channel)
|
||||
|
||||
def setwarnings(val):
|
||||
if conf[TEST]:
|
||||
pass
|
||||
else:
|
||||
GPIO.setwarnings(val)
|
||||
|
||||
11
src/gui-client/runGui.bat
Normal file
11
src/gui-client/runGui.bat
Normal file
@ -0,0 +1,11 @@
|
||||
:: Anaconda3 path
|
||||
set root=C:\ProgramData\Anaconda3
|
||||
|
||||
:: Program path
|
||||
set guiPath=C:\Users\Jan\Documents\Projekte\TimeBinControl\src\gui-client
|
||||
|
||||
call %root%\Scripts\activate.bat %root%
|
||||
|
||||
call cd /D %guiPath%
|
||||
|
||||
python client.py
|
||||
1
src/gui-client/settings.json
Normal file
1
src/gui-client/settings.json
Normal file
@ -0,0 +1 @@
|
||||
{"defaultIP": "134.96.13.250", "defaultPort": 2000, "ch0": {"name": "Encoder", "setpoint": 26.62, "kp": 12.0, "ki": 4.0, "kd": 0, "PIDenabled": 0, "channelEnabled": 1}, "ch1": {"name": "Decoder", "setpoint": 26.62, "kp": 12.0, "ki": 4.0, "kd": 0, "PIDenabled": 0, "channelEnabled": 1}, "ch2": {"name": "Channel 3", "setpoint": 34.05, "kp": 0.0, "ki": 0.0, "kd": 0.0, "PIDenabled": 0, "channelEnabled": 1}, "ch3": {"name": "Channel 4", "setpoint": 21.5, "kp": 0.0, "ki": 0.0, "kd": 0.0, "PIDenabled": 0, "channelEnabled": 1}}
|
||||
31
src/gui-client/tcptools.py
Normal file
31
src/gui-client/tcptools.py
Normal file
@ -0,0 +1,31 @@
|
||||
import struct
|
||||
import socket
|
||||
|
||||
def send_msg(sock, msg):
|
||||
# Prefix each message with a 4-byte length (network byte order)
|
||||
msg = str(msg)
|
||||
msg = struct.pack('>I', len(msg)) + msg.encode()
|
||||
sock.sendall(msg)
|
||||
|
||||
def send_cmd(sock, msg):
|
||||
msg = msg + ' '*(1024 - len(msg))
|
||||
sock.send((msg.encode()))
|
||||
|
||||
def recv_msg(sock):
|
||||
# Read message length and unpack it into an integer
|
||||
raw_msglen = sock.recv(4)
|
||||
if not raw_msglen:
|
||||
return None
|
||||
msglen = struct.unpack('>I', raw_msglen)[0]
|
||||
# Read the message data
|
||||
return recvall(sock, msglen)
|
||||
|
||||
def recvall(sock, n):
|
||||
# Helper function to recv n bytes or return None if EOF is hit
|
||||
data = ''
|
||||
while len(data.encode()) < n:
|
||||
packet = sock.recv(n - len(data))
|
||||
if not packet:
|
||||
return None
|
||||
data = packet.decode()
|
||||
return data
|
||||
147
src/server.py
Normal file
147
src/server.py
Normal file
@ -0,0 +1,147 @@
|
||||
import sys
|
||||
import time
|
||||
import socket
|
||||
import threading
|
||||
|
||||
import queue
|
||||
import zmq
|
||||
|
||||
import pin as GPIO
|
||||
GPIO.config('./config.json')
|
||||
|
||||
import MAX11270 as adc
|
||||
import mcp4822 as dac
|
||||
import PID as pid
|
||||
import tcptools as TCP
|
||||
import spithread as SPIThread
|
||||
import clientthread as clientThread
|
||||
import globalvars
|
||||
######################################################################
|
||||
#TCP/IP
|
||||
HOST = '' #Symbolic name
|
||||
PORT = 2000
|
||||
|
||||
######################################################################
|
||||
# GPIO conf
|
||||
GPIO.setmode(GPIO.BCM)
|
||||
ADC_CLK_PIN = 21
|
||||
ADC_MOSI_PIN = 20
|
||||
ADC_MISO_PIN = 19
|
||||
ADC_CS_PIN = 17 #ADC 0 -> input 0,1
|
||||
ADC_SYNC_PIN = 27
|
||||
ADC_RSTB_PIN = 22
|
||||
|
||||
DAC_CLK_PIN = 11
|
||||
DAC_MOSI_PIN = 10
|
||||
DAC_CS_PIN = 8
|
||||
DAC_LDAC_PIN = 5
|
||||
|
||||
|
||||
#----nonglobals-------
|
||||
# this is the array of threads
|
||||
t = [] # this is the array of threads
|
||||
|
||||
######################################################################
|
||||
|
||||
#Workaround for Windows to handle KeyboardInterrupt
|
||||
def workaround():
|
||||
try:
|
||||
while True: input()
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
socket.socket().connect((HOST,PORT))
|
||||
######################################################################
|
||||
#start program
|
||||
|
||||
# Setting up server
|
||||
lock = threading.Lock() #create lock functionalities
|
||||
print('\nCreate socket')
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#create socket
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
|
||||
print(' ...done')
|
||||
print('\nBind socket...')
|
||||
#bind socket
|
||||
try:
|
||||
sock.bind((HOST,PORT))
|
||||
except socket.error as msg:
|
||||
print(' ...Bind failed. Error Code : '+str(msg.errno)+' Message : '+ msg.strerror)
|
||||
sock.close()
|
||||
sys.exit()
|
||||
print(' ...done')
|
||||
print('\nOpen socket listener...')
|
||||
sock.listen(10)
|
||||
print(' ...done')
|
||||
|
||||
# Setting up I/O
|
||||
adc0 = adc.MAX11270(ADC_CLK_PIN, ADC_MOSI_PIN, ADC_MISO_PIN, ADC_CS_PIN, ADC_SYNC_PIN, ADC_RSTB_PIN)
|
||||
|
||||
|
||||
dac0 = dac.MCP4822(DAC_CLK_PIN, DAC_MOSI_PIN, DAC_CS_PIN, DAC_LDAC_PIN, nullVoltage=1.29)
|
||||
|
||||
|
||||
pid_obj = [pid.PID(0, 0, 0, 1, 0)]
|
||||
|
||||
|
||||
#Start spithread
|
||||
#new_t = threading.Thread(target=SPIThread.spithread,args=([adc0, adc1], [dac0, dac1], SPIQueue, pidqueue))
|
||||
new_t = threading.Thread(target=SPIThread.spithread,args=([adc0], [dac0],))
|
||||
new_t.deamon = True
|
||||
new_t.start()
|
||||
t.append(new_t) #append to array of threads
|
||||
|
||||
#Start pid thread
|
||||
for i in range(len(pid_obj)):
|
||||
new_t = threading.Thread(target=pid.run,args=(pid_obj[i], 100,i))
|
||||
new_t.deamon = True
|
||||
new_t.start()
|
||||
t.append(new_t) #append to array of threads
|
||||
|
||||
#Start workaround only on Windows
|
||||
if sys.platform.startswith('win'):
|
||||
new_t = threading.Thread(target=workaround,args=())
|
||||
new_t.deamon = True
|
||||
new_t.start()
|
||||
t.append(new_t) #append to array of threads
|
||||
|
||||
print('\nStart waiting for connections...')
|
||||
while True:
|
||||
conn = None
|
||||
try:
|
||||
#wait to accept a connection - blocking call
|
||||
print('\nWait for connection...')
|
||||
conn, addr = sock.accept()
|
||||
print('Connected with ' + addr[0] + ':' + str(addr[1]))
|
||||
print('\nOpen client thread...')
|
||||
new_t = threading.Thread(target=clientThread.clientthread,args=(conn,))
|
||||
new_t.deamon = True
|
||||
new_t.start()
|
||||
t.append(new_t) #append to array of threads
|
||||
print(' ...done')
|
||||
print('\nCleanup threads...')
|
||||
t = [this_t for this_t in t if this_t.is_alive()]#cleanup t
|
||||
print(' ...done')
|
||||
|
||||
except (KeyboardInterrupt,SystemExit): #Ctrl + break for keyboard interrupt (windows)
|
||||
print('\nKeyboard interrupt...')
|
||||
print('\nCleanup threads...')
|
||||
t = [this_t for this_t in t if this_t.is_alive()]#cleanup t
|
||||
print(' ...done')
|
||||
print('\nClosing socket...')
|
||||
sock.close()
|
||||
print(' ...done')
|
||||
print('\nWaiting for threads to close...')
|
||||
globalvars.stopall = True
|
||||
|
||||
#Send end command to all threads
|
||||
#SPIQueue.put(['END'])
|
||||
#Connect to SPI thread via zmq tcp socket
|
||||
context = zmq.Context()
|
||||
SPIclient = context.socket(zmq.REQ)
|
||||
SPIclient.connect("tcp://localhost:5555")
|
||||
SPIclient.send("END\t".encode())
|
||||
msg = SPIclient.recv()
|
||||
|
||||
for this_t in t: #wait for threads to close
|
||||
this_t.join()
|
||||
print(' ...done')
|
||||
GPIO.cleanup()
|
||||
sys.exit()
|
||||
112
src/spithread.py
Normal file
112
src/spithread.py
Normal file
@ -0,0 +1,112 @@
|
||||
import queue
|
||||
import zmq
|
||||
import math
|
||||
import MAX11270 as adc
|
||||
import MCP4822 as dac
|
||||
|
||||
import globalvars
|
||||
|
||||
def adc2T(dU):
|
||||
#constants
|
||||
#VREF = (REF+) – (REF–)
|
||||
#Vref = 2.5
|
||||
#wheatstonebridge
|
||||
# U0_____
|
||||
# |
|
||||
# R0
|
||||
# Uw_____|__
|
||||
# | |
|
||||
# R2 R4
|
||||
# |__dU__|
|
||||
# | |
|
||||
# R1 R3
|
||||
# |______|
|
||||
# Gnd_____|_
|
||||
#
|
||||
# R1 = resistance of thermistor
|
||||
R0 = 0
|
||||
R2 = 5100 #(Ohm)
|
||||
R3 = 97600 #(Ohm)
|
||||
R4 = 5100 #(Ohm)
|
||||
U0 = 3 #(V)
|
||||
#steinhart-Hart Coefficients 10uA
|
||||
# NTC_A = 9.7142e-4;
|
||||
# NTC_B = 2.3268e-4;
|
||||
# NTC_C = 8.0591e-8;
|
||||
#steinhart-Hart Coefficients 100uA
|
||||
NTC_A = 9.6542e-4
|
||||
NTC_B = 2.3356e-4
|
||||
NTC_C = 7.7781e-8
|
||||
|
||||
R1 = -(R0*R2*dU - R2*R3*U0 + R0*R3*dU + R0*R4*dU + R2*R3*dU + R2*R4*dU)/(R4*U0 + R0*dU + R3*dU + R4*dU)
|
||||
#calculate temperature
|
||||
try:
|
||||
T = 1/(NTC_A + NTC_B*math.log(float(R1)) + NTC_C*math.pow(math.log(float(R1)),3)) - 273.15
|
||||
except ValueError:
|
||||
T = 6666
|
||||
return T
|
||||
|
||||
#Thread for handling communication with spi devices
|
||||
#def spithread(adc, dac,in_queue,out_queue):
|
||||
def spithread(adc, dac):
|
||||
context = zmq.Context()
|
||||
server = context.socket(zmq.REP)
|
||||
server.bind("tcp://*:5555")
|
||||
|
||||
stop = False
|
||||
while not globalvars.stopall and not stop:
|
||||
cmd_recv = server.recv() #Blocking call, wait for a new message
|
||||
|
||||
ans = 'NAK'
|
||||
msg = cmd_recv.decode().split('\t')
|
||||
#msg = in_queue.get() #Blocking call, waits for an element
|
||||
try:
|
||||
msg[1] = int(msg[1])
|
||||
msg[2] = int(msg[2])
|
||||
msg[3] = int(msg[3])
|
||||
except (IndexError, ValueError):
|
||||
pass
|
||||
|
||||
if msg[0] == 'ADC':
|
||||
if msg[1] <= 1:
|
||||
if msg[2] <= 1:
|
||||
try:
|
||||
T = adc2T(adc[msg[1]].read_differential())
|
||||
except TimeoutError:
|
||||
print("Conversion takes to long!")
|
||||
T = 100
|
||||
if msg[3] <= 5:
|
||||
ans = str(T)
|
||||
#out_queue[msg[3]].put(T)
|
||||
else:
|
||||
print('Error: Invalid output queue!')
|
||||
else:
|
||||
print('Error: Invalid device channel!')
|
||||
else:
|
||||
print('Error: Invalid device number!')
|
||||
|
||||
elif msg[0] == 'DAC':
|
||||
if msg[1] <= 1:
|
||||
if msg[2] <= 1:
|
||||
if msg[2] == 1:
|
||||
voltage = float(msg[4]) - dac[msg[1]].nullVoltage
|
||||
T = dac[msg[1]].write(voltage,msg[2])
|
||||
else:
|
||||
T = dac[msg[1]].write(float(msg[4]),msg[2])
|
||||
if msg[3] <= 5:
|
||||
#out_queue[msg[3]].put('ACK')
|
||||
ans = 'ACK'
|
||||
else:
|
||||
print('Error: Invalid output queue!')
|
||||
else:
|
||||
print('Error: Invalid device channel!')
|
||||
else:
|
||||
print('Error: Invalid device number!')
|
||||
elif msg[0] == 'END':
|
||||
ans = 'ACK'
|
||||
stop = True
|
||||
else:
|
||||
print('Error: Invalid device!')
|
||||
|
||||
#zmq ensure that the answer is delivered to the right clientthread
|
||||
server.send(ans.encode())
|
||||
31
src/tcptools.py
Normal file
31
src/tcptools.py
Normal file
@ -0,0 +1,31 @@
|
||||
import struct
|
||||
import socket
|
||||
|
||||
def send_msg(sock, msg):
|
||||
# Prefix each message with a 4-byte length (network byte order)
|
||||
msg = str(msg)
|
||||
msg = struct.pack('>I', len(msg)) + msg.encode()
|
||||
sock.sendall(msg)
|
||||
|
||||
def send_cmd(sock, msg):
|
||||
msg = msg + ' '*(1024 - len(msg))
|
||||
sock.send((msg.encode()))
|
||||
|
||||
def recv_msg(sock):
|
||||
# Read message length and unpack it into an integer
|
||||
raw_msglen = sock.recv(4)
|
||||
if not raw_msglen:
|
||||
return None
|
||||
msglen = struct.unpack('>I', raw_msglen)[0]
|
||||
# Read the message data
|
||||
return recvall(sock, msglen)
|
||||
|
||||
def recvall(sock, n):
|
||||
# Helper function to recv n bytes or return None if EOF is hit
|
||||
data = ''
|
||||
while len(data.encode()) < n:
|
||||
packet = sock.recv(n - len(data))
|
||||
if not packet:
|
||||
return None
|
||||
data = packet.decode()
|
||||
return data
|
||||
Loading…
x
Reference in New Issue
Block a user