From 4c96bf5c7e4cea2702acbcc3856090891f3c7d52 Mon Sep 17 00:00:00 2001 From: "sdt.tutorial" Date: Wed, 24 Apr 2024 11:24:33 +0900 Subject: [PATCH] Commit message --- .vscode/launch.json | 15 + MainWindow.py | 87 ++++++ MainWindow.ui | 216 ++++++++++++++ SDT_Device/CCU.py | 185 ++++++++++++ SDT_Device/DTS.py | 148 ++++++++++ SDT_Device/Protocol.py | 266 ++++++++++++++++++ SDT_Device/PulseGenerator.py | 237 ++++++++++++++++ SDT_Device/__init__.py | 1 + SDT_Device/__pycache__/CCU.cpython-311.pyc | Bin 0 -> 9031 bytes SDT_Device/__pycache__/CCU.cpython-312.pyc | Bin 0 -> 11000 bytes SDT_Device/__pycache__/DTS.cpython-311.pyc | Bin 0 -> 8033 bytes SDT_Device/__pycache__/DTS.cpython-312.pyc | Bin 0 -> 8050 bytes SDT_Device/__pycache__/DTS.cpython-38.pyc | Bin 0 -> 5050 bytes .../__pycache__/Protocol.cpython-311.pyc | Bin 0 -> 6090 bytes .../__pycache__/Protocol.cpython-312.pyc | Bin 0 -> 13017 bytes .../__pycache__/Protocol.cpython-38.pyc | Bin 0 -> 7074 bytes .../PulseGenerator.cpython-311.pyc | Bin 0 -> 10105 bytes .../PulseGenerator.cpython-312.pyc | Bin 0 -> 11850 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 178 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 187 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 165 bytes client.py | 48 ++++ framework.yaml | 12 + main.py | 124 ++++++++ requirements.txt | 6 + server.py | 26 ++ test.py | 55 ++++ 27 files changed, 1426 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 MainWindow.py create mode 100644 MainWindow.ui create mode 100644 SDT_Device/CCU.py create mode 100644 SDT_Device/DTS.py create mode 100644 SDT_Device/Protocol.py create mode 100644 SDT_Device/PulseGenerator.py create mode 100644 SDT_Device/__init__.py create mode 100644 SDT_Device/__pycache__/CCU.cpython-311.pyc create mode 100644 SDT_Device/__pycache__/CCU.cpython-312.pyc create mode 100644 SDT_Device/__pycache__/DTS.cpython-311.pyc create mode 100644 SDT_Device/__pycache__/DTS.cpython-312.pyc create mode 100644 SDT_Device/__pycache__/DTS.cpython-38.pyc create mode 100644 SDT_Device/__pycache__/Protocol.cpython-311.pyc create mode 100644 SDT_Device/__pycache__/Protocol.cpython-312.pyc create mode 100644 SDT_Device/__pycache__/Protocol.cpython-38.pyc create mode 100644 SDT_Device/__pycache__/PulseGenerator.cpython-311.pyc create mode 100644 SDT_Device/__pycache__/PulseGenerator.cpython-312.pyc create mode 100644 SDT_Device/__pycache__/__init__.cpython-311.pyc create mode 100644 SDT_Device/__pycache__/__init__.cpython-312.pyc create mode 100644 SDT_Device/__pycache__/__init__.cpython-38.pyc create mode 100644 client.py create mode 100644 framework.yaml create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 server.py create mode 100644 test.py diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..3d1a6e1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/MainWindow.py b/MainWindow.py new file mode 100644 index 0000000..d5cda81 --- /dev/null +++ b/MainWindow.py @@ -0,0 +1,87 @@ +# Form implementation generated from reading ui file '.\MainWindow.ui' +# +# Created by: PyQt6 UI code generator 6.6.1 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(1251, 650) + self.centralwidget = QtWidgets.QWidget(parent=MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.graphWidget1 = PlotWidget(parent=self.centralwidget) + self.graphWidget1.setGeometry(QtCore.QRect(150, 40, 1061, 561)) + self.graphWidget1.setObjectName("graphWidget1") + self.pb_Start1 = QtWidgets.QPushButton(parent=self.centralwidget) + self.pb_Start1.setGeometry(QtCore.QRect(20, 440, 93, 28)) + self.pb_Start1.setObjectName("pb_Start1") + self.pb_Stop1 = QtWidgets.QPushButton(parent=self.centralwidget) + self.pb_Stop1.setGeometry(QtCore.QRect(20, 470, 93, 28)) + self.pb_Stop1.setObjectName("pb_Stop1") + self.lineEdit_EnableDelayValue = QtWidgets.QLineEdit(parent=self.centralwidget) + self.lineEdit_EnableDelayValue.setGeometry(QtCore.QRect(20, 100, 113, 21)) + self.lineEdit_EnableDelayValue.setObjectName("lineEdit_EnableDelayValue") + self.label = QtWidgets.QLabel(parent=self.centralwidget) + self.label.setGeometry(QtCore.QRect(20, 80, 101, 16)) + self.label.setObjectName("label") + self.pushButton_EnableDelaySet = QtWidgets.QPushButton(parent=self.centralwidget) + self.pushButton_EnableDelaySet.setGeometry(QtCore.QRect(20, 130, 93, 28)) + self.pushButton_EnableDelaySet.setObjectName("pushButton_EnableDelaySet") + self.pushButton_AverageStepSet = QtWidgets.QPushButton(parent=self.centralwidget) + self.pushButton_AverageStepSet.setGeometry(QtCore.QRect(20, 230, 93, 28)) + self.pushButton_AverageStepSet.setObjectName("pushButton_AverageStepSet") + self.label_2 = QtWidgets.QLabel(parent=self.centralwidget) + self.label_2.setGeometry(QtCore.QRect(20, 180, 101, 16)) + self.label_2.setObjectName("label_2") + self.pushButton_SampleRangeSet = QtWidgets.QPushButton(parent=self.centralwidget) + self.pushButton_SampleRangeSet.setGeometry(QtCore.QRect(20, 330, 93, 28)) + self.pushButton_SampleRangeSet.setObjectName("pushButton_SampleRangeSet") + self.lineEdit_SampleRangeValue = QtWidgets.QLineEdit(parent=self.centralwidget) + self.lineEdit_SampleRangeValue.setGeometry(QtCore.QRect(20, 300, 113, 21)) + self.lineEdit_SampleRangeValue.setObjectName("lineEdit_SampleRangeValue") + self.label_3 = QtWidgets.QLabel(parent=self.centralwidget) + self.label_3.setGeometry(QtCore.QRect(20, 280, 101, 16)) + self.label_3.setObjectName("label_3") + self.comboBox_AverageStepVal = QtWidgets.QComboBox(parent=self.centralwidget) + self.comboBox_AverageStepVal.setGeometry(QtCore.QRect(20, 200, 94, 22)) + self.comboBox_AverageStepVal.setEditable(False) + self.comboBox_AverageStepVal.setObjectName("comboBox_AverageStepVal") + self.lineEdit_TriggerCount = QtWidgets.QLineEdit(parent=self.centralwidget) + self.lineEdit_TriggerCount.setEnabled(True) + self.lineEdit_TriggerCount.setGeometry(QtCore.QRect(20, 390, 113, 21)) + self.lineEdit_TriggerCount.setReadOnly(True) + self.lineEdit_TriggerCount.setObjectName("lineEdit_TriggerCount") + self.label_4 = QtWidgets.QLabel(parent=self.centralwidget) + self.label_4.setGeometry(QtCore.QRect(20, 370, 101, 16)) + self.label_4.setObjectName("label_4") + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(parent=MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1251, 26)) + self.menubar.setObjectName("menubar") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(parent=MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) + self.pb_Start1.setText(_translate("MainWindow", "Start")) + self.pb_Stop1.setText(_translate("MainWindow", "Stop")) + self.label.setText(_translate("MainWindow", "Enable Delay ")) + self.pushButton_EnableDelaySet.setText(_translate("MainWindow", "set")) + self.pushButton_AverageStepSet.setText(_translate("MainWindow", "set")) + self.label_2.setText(_translate("MainWindow", "Average")) + self.pushButton_SampleRangeSet.setText(_translate("MainWindow", "set")) + self.label_3.setText(_translate("MainWindow", "Sample Range")) + self.label_4.setText(_translate("MainWindow", "Trigger Count")) +from pyqtgraph import PlotWidget diff --git a/MainWindow.ui b/MainWindow.ui new file mode 100644 index 0000000..30075c3 --- /dev/null +++ b/MainWindow.ui @@ -0,0 +1,216 @@ + + + MainWindow + + + + 0 + 0 + 1251 + 650 + + + + MainWindow + + + + + + 150 + 40 + 1061 + 561 + + + + + + + 20 + 440 + 93 + 28 + + + + Start + + + + + + 20 + 470 + 93 + 28 + + + + Stop + + + + + + 20 + 100 + 113 + 21 + + + + + + + 20 + 80 + 101 + 16 + + + + Enable Delay + + + + + + 20 + 130 + 93 + 28 + + + + set + + + + + + 20 + 230 + 93 + 28 + + + + set + + + + + + 20 + 180 + 101 + 16 + + + + Average + + + + + + 20 + 330 + 93 + 28 + + + + set + + + + + + 20 + 300 + 113 + 21 + + + + + + + 20 + 280 + 101 + 16 + + + + Sample Range + + + + + + 20 + 200 + 94 + 22 + + + + false + + + + + true + + + + 20 + 390 + 113 + 21 + + + + true + + + + + + 20 + 370 + 101 + 16 + + + + Trigger Count + + + + + + + 0 + 0 + 1251 + 26 + + + + + + + + PlotWidget + QWidget +
pyqtgraph
+ 1 +
+
+ + +
diff --git a/SDT_Device/CCU.py b/SDT_Device/CCU.py new file mode 100644 index 0000000..5e25432 --- /dev/null +++ b/SDT_Device/CCU.py @@ -0,0 +1,185 @@ +from SDT_Device.Protocol import Protocol +import threading +import time + + +class CCU(Protocol): + MAX_CHANNEL = 20 + + CMD = { + "PULSE_COUNT": 0x00, + "COINCIDENCE_COUNT": 0x01, + "INPUT_DELAY": 0x10, + "PULSE_WIDTH": 0x20, + "COINCIDENCE_CH_SELECT": 0x30, + "UPDATE_TIMER_PERIOD": 0x40, + "UPDATE_TRIG_SELECT": 0x50, + "START": 0x60, + "COUNTER_MUX": 0x70, + "INPUT_CH_MUX": 0x80, + "HEART_BIT": 0xF000, + } + + def __init__(self, ip, port): + super().__init__(ip, port) + + self.setDeviceId(self.DEVICE_ID["CCU"]) + self.setDeviceSerial(0) + self.classGetCallback = self._classGetCallback + self.classDataCallback = self._classDataCallback + + self._getEvent = threading.Event() + self._dataEvent = threading.Event() + + self._readBuffer = [] + self._pulseCount = [0 for i in range(20)] + self._coincidenceCount = [0 for i in range(20)] + self.updateCount = 0 + self.updateCoinCount = 0 + + self.debugCount = 0 + + def _classGetCallback(self, command, payload): + self._readBuffer = payload + self._getEvent.set() + + def _read(self): + self._getEvent.wait() + self._getEvent.clear() + return self._readBuffer + + def _classDataCallback(self, command, payload): + if command == self.CMD["PULSE_COUNT"]: + self._pulseCount = payload + self.updateCount += 1 + elif command == self.CMD["COINCIDENCE_COUNT"]: + self._coincidenceCount = payload + self.updateCoinCount += 1 + self._dataEvent.set() + + def readCountAllNonBlocking(self): + return self._pulseCount, self._coincidenceCount + + def readCountAllBlocking(self): + self._dataEvent.wait() + self._dataEvent.clear() + return self._pulseCount, self._coincidenceCount + + def setInputDelay(self, ch, delay): + delayList = self.getInputDelayAll() + delayList[ch] = delay + args = (self.CLASS["SET"], self.CMD["INPUT_DELAY"], delayList) + self.send(args) + + def setInputDelayAll(self, delay: list): + args = (self.CLASS["SET"], self.CMD["INPUT_DELAY"], delay) + self.send(args) + + def getInputDelay(self, ch): + return self.getInputDelayAll()[ch] + + def getInputDelayAll(self): + args = (self.CLASS["GET"], self.CMD["INPUT_DELAY"]) + self.send(args) + return self._read() + + def setPulseWidth(self, ch, width): + widthList = self.getPulseWidthAll() + widthList[ch] = width + args = (self.CLASS["SET"], self.CMD["PULSE_WIDTH"], widthList) + self.send(args) + + def setPulseWidthAll(self, width: list): + args = (self.CLASS["SET"], self.CMD["PULSE_WIDTH"], width) + self.send(args) + + def getPulseWidth(self, ch): + return self.getPulseWidthAll()[ch] + + def getPulseWidthAll(self): + args = (self.CLASS["GET"], self.CMD["PULSE_WIDTH"]) + self.send(args) + return self._read() + + def setCoincidenceChSelect(self, ch, chSelect): + coincidenceList = self.getCoincidenceChSelectAll() + coincidenceList[ch] = chSelect + args = (self.CLASS["SET"], self.CMD["COINCIDENCE_CH_SELECT"], coincidenceList) + self.send(args) + + def setCoincidenceChSelectAll(self, chSelect: list): + args = (self.CLASS["SET"], self.CMD["COINCIDENCE_CH_SELECT"], chSelect) + self.send(args) + + def getCoincidenceChSelect(self, ch): + return self.getCoincidenceChSelectAll()[ch] + + def getCoincidenceChSelectAll(self): + args = (self.CLASS["GET"], self.CMD["COINCIDENCE_CH_SELECT"]) + self.send(args) + return self._read() + + def setUpdateTimerPeriod(self, period): + args = (self.CLASS["SET"], self.CMD["UPDATE_TIMER_PERIOD"], [period]) + self.send(args) + + def getUpdateTimerPeriod(self): + args = (self.CLASS["GET"], self.CMD["UPDATE_TIMER_PERIOD"]) + self.send(args) + return self._read()[0] + + def setTrigSelect(self, trigType): + args = (self.CLASS["SET"], self.CMD["UPDATE_TRIG_SELECT"], [trigType]) + self.send(args) + + def getTrigSelect(self): + args = (self.CLASS["GET"], self.CMD["UPDATE_TRIG_SELECT"]) + self.send(args) + return self._read() + + def setStart(self, enable): + args = (self.CLASS["SET"], self.CMD["START"], [enable]) + self.send(args) + + def getStart(self): + args = (self.CLASS["GET"], self.CMD["START"]) + self.send(args) + return self._read() + + # val = 0 -> use internal pulse window + # val = 1 -> use external pulse window + def setCounterMux(self, ch, val): + valList = self.getCounterMuxAll() + valList[ch] = val + args = (self.CLASS["SET"], self.CMD["COUNTER_MUX"], valList) + self.send(args) + + def setCounterMuxAll(self, val: list): + args = (self.CLASS["SET"], self.CMD["COUNTER_MUX"], val) + self.send(args) + + def getCounterMux(self, ch): + return self.getCounterMuxAll()[ch] + + def getCounterMuxAll(self): + args = (self.CLASS["GET"], self.CMD["COUNTER_MUX"]) + self.send(args) + return self._read() + + def setInputChMux(self, ch, val): + valList = self.getInputChMuxAll() + valList[ch] = val + args = (self.CLASS["SET"], self.CMD["INPUT_CH_MUX"], valList) + self.send(args) + + def setInputChMuxAll(self, val: list): + args = (self.CLASS["SET"], self.CMD["INPUT_CH_MUX"], val) + self.send(args) + + def getInputChMux(self, ch): + return self.getInputChMuxAll()[ch] + + def getInputChMuxAll(self): + args = (self.CLASS["GET"], self.CMD["INPUT_CH_MUX"]) + self.send(args) + return self._read() diff --git a/SDT_Device/DTS.py b/SDT_Device/DTS.py new file mode 100644 index 0000000..0427ef2 --- /dev/null +++ b/SDT_Device/DTS.py @@ -0,0 +1,148 @@ +from SDT_Device.Protocol import Protocol +import threading +import copy +import timeit + + +class DTS(Protocol): + MAX_CHANNEL = 4 + + CMD = { + "ADC_CH_DATA_BASE": 0x00, + "SYSTEM_RESET": 0x10, + "DEBUG_DATA": 0x20, + "AVG_ENABLE_DELAY": 0x30, + "AVG_SAMPLE_RANGE": 0x31, + "AVG_STEP": 0x32, + "AVG_START": 0x33, + "AVG_TIMER_TICK": 0x34, + "HEART_BIT": 0xF000, + } + + def __init__(self, ip, port): + super().__init__(ip, port) + + self.setDeviceId(self.DEVICE_ID["DTS"]) + self.setDeviceSerial(0) + self.setMaxLen(4 * 65536 + 20) + self.classGetCallback = self._classGetCallback + self.classDataCallback = self._classDataCallback + self.classHeartBitCallback = self._classHeartBitCallback + + self._getEvnet = threading.Event() + self._dataEvnet = threading.Event() + + self._readBuffer = [] + self.adcDataCh0 = [] + self.adcDataCh1 = [] + self.adcDataCh2 = [] + self.adcDataCh3 = [] + + self.hbCount = 0 + self.dataCount = [0 for _ in range(4)] + + def _classHeartBitCallback(self): + self.hbCount += 1 + + def _classGetCallback(self, command, payload): + self._readBuffer = payload + self._getEvnet.set() + + def _read(self): + self._getEvnet.wait() + self._getEvnet.clear() + return self._readBuffer + + def _classDataCallback(self, command, payload): + if command == (self.CMD["ADC_CH_DATA_BASE"] + 0): + self.adcDataCh0 = payload + self.dataCount[0] += 1 + elif command == (self.CMD["ADC_CH_DATA_BASE"] + 1): + self.adcDataCh1 = payload + self.dataCount[1] += 1 + elif command == (self.CMD["ADC_CH_DATA_BASE"] + 2): + self.adcDataCh2 = payload + self.dataCount[2] += 1 + elif command == (self.CMD["ADC_CH_DATA_BASE"] + 3): + self.adcDataCh3 = payload + self.dataCount[3] += 1 + + # self._dataEvnet.set() + + def ReadAdcData(self, ch): + # self._waitAdcData() + ret = [] + + if ch == 0: + ret = self.adcDataCh0 + elif ch == 1: + ret = self.adcDataCh1 + elif ch == 2: + ret = self.adcDataCh2 + elif ch == 3: + ret = self.adcDataCh3 + + return ret + + def _waitAdcData(self): + self._dataEvnet.wait() + self._dataEvnet.clear() + + def setSystemReset(self): # 1번 + args = (self.CLASS["SET"], self.CMD["SYSTEM_RESET"], [1]) + self.send(args) + + def getSystemReset(self): + args = (self.CLASS["GET"], self.CMD["SYSTEM_RESET"]) + self.send(args) + return self._read()[0] + + def getDebugData(self): + args = (self.CLASS["GET"], self.CMD["DEBUG_DATA"]) + self.send(args) + return self._read() + + def setAvgEnableDelay(self, ch, val): # 3번 + args = (self.CLASS["SET"], self.CMD["AVG_ENABLE_DELAY"], [ch, val]) + self.send(args) + + def getAvgEnableDelay(self, ch): + args = (self.CLASS["GET"], self.CMD["AVG_ENABLE_DELAY"], [ch]) + self.send(args) + return self._read()[1] + + def setAvgSampleRange(self, ch, val): # 4번 + args = (self.CLASS["SET"], self.CMD["AVG_SAMPLE_RANGE"], [ch, val]) + self.send(args) + + def getAvgSampleRange(self, ch): + args = (self.CLASS["GET"], self.CMD["AVG_SAMPLE_RANGE"], [ch]) + self.send(args) + return self._read()[1] + + def setAvgStep(self, ch, val): # 5번 + args = (self.CLASS["SET"], self.CMD["AVG_STEP"], [ch, val]) + self.send(args) + + def getAvgStep(self, ch): + args = (self.CLASS["GET"], self.CMD["AVG_STEP"], [ch]) + self.send(args) + return self._read()[1] + + def setAvgStart(self, ch, val): # 2번 변수 바꿔서 6번 + args = (self.CLASS["SET"], self.CMD["AVG_START"], [ch, val]) + self.send(args) + + def getAvgStart(self, ch): + args = (self.CLASS["GET"], self.CMD["AVG_START"], [ch]) + self.send(args) + return self._read()[1] + + def setTimerTick(self, ch, val): # 7번 + args = (self.CLASS["SET"], self.CMD["AVG_TIMER_TICK"], [ch, val]) + self.send(args) + + def getTimerTick(self, ch): + args = (self.CLASS["GET"], self.CMD["AVG_TIMER_TICK"], [ch]) + self.send(args) + return self._read()[1] diff --git a/SDT_Device/Protocol.py b/SDT_Device/Protocol.py new file mode 100644 index 0000000..05e9bb2 --- /dev/null +++ b/SDT_Device/Protocol.py @@ -0,0 +1,266 @@ +import struct +import socket +import threading +from enum import Enum +from queue import Queue + + +class State(Enum): + STX = 0 + LEN = 1 + DEVICE_ID = 2 + DEVICE_SERIAL = 3 + CLASS = 4 + COMMAND = 5 + DATA = 6 + ETX = 7 + + +class Packet: + def __init__(self): + self.stx = 0 + self.len = 0 + self.deviceId = 0 + self.deviceSerial = 0 + self.classType = 0 + self.command = 0 + self.data = [] + self.etx = 0 + + +class Protocol: + DEVICE_ID = {"CCU": 0x1, "TTMU": 0x2, "PG": 0x3, "QC": 0x4, "DTS": 0x5} + CLASS = { + "DATA": 0x44, + "GET": 0x47, + "SET": 0x53, + "EVENT": 0x45, + "HEART_BIT": 0x48, + } + + HEADER_LEN = 20 + STX = 0x2 + ETX = 0x3 + + def __init__(self, ip, port): + self.state = State.STX + self.dataIndex = 0 + self.deviceId = 0 + self.deviceSerial = 0 + self.packet = Packet() + self.maxLen = 0 + + self.recvCount = 0 + + self.classGetCallback = None + self.classHeartBitCallback = None + self.classDataCallback = None + + self.dataQueue = Queue() + + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sock.connect((ip, port)) + + recv_thread = threading.Thread(target=self._recvThread) + recv_thread.daemon = True + recv_thread.start() + + parsing_thread = threading.Thread(target=self._parsingThread) + parsing_thread.daemon = True + parsing_thread.start() + + def setMaxLen(self, len): + self.maxLen = len + + def setDeviceId(self, deviceId): + self.deviceId = deviceId + + def getDeviceId(self): + return self.deviceId + + def setDeviceSerial(self, deviceSerial): + self.deviceSerial = deviceSerial + + def getDeviceSerial(self): + return self.deviceSerial + + def send(self, arg): + print("Build: ", self._buildPacket(*arg)) + # print("Build: ", int.from_bytes(self._buildPacket(*arg), byteorder='little')) + self.sock.sendall(self._buildPacket(*arg)) + + def _buildPacket(self, classType, cmd, data=None): + length = self.HEADER_LEN + print(f"Build Packet: {classType} / {cmd}") + payload = [] + if data is not None: + payload = data + length = self.HEADER_LEN + 4 * len(payload) + + string_format = f"<{7+len(payload)}I" + return struct.pack( + string_format, + self.STX, + length, + self.deviceId, + self.deviceSerial, + classType, + cmd, + *payload, + self.ETX, + ) + + def parsing(self, word): + ret = False + + # print("[TEST] Parsing") + switch_dict = { + State.STX: self.case_stx, + State.LEN: self.case_len, + State.DEVICE_ID: self.case_producId, + State.DEVICE_SERIAL: self.case_productSerial, + State.CLASS: self.case_class, + State.COMMAND: self.case_command, + State.DATA: self.case_data, + State.ETX: self.case_etx, + } + + print(f"Parsing {self.state} / {word}") + if self.state in switch_dict: + ret = switch_dict[self.state](word) + else: + self.case_default() + + return ret + + def case_stx(self, word): + if word == self.STX: + self.dataIndex = 0 + self.packet = Packet() + self.packet.stx = word + self.state = State.LEN + return False + + def case_len(self, word): + self.packet.len = word + if 20 <= self.packet.len <= self.maxLen: + self.state = State.DEVICE_ID + else: + self.state = State.STX + return False + + def case_producId(self, word): + self.packet.deviceId = word + if self.packet.deviceId == self.deviceId: + self.state = State.DEVICE_SERIAL + else: + self.state = State.STX + return False + + def case_productSerial(self, word): + self.packet.deviceSerial = word + if self.packet.deviceSerial == self.deviceSerial: + self.state = State.CLASS + else: + self.state = State.STX + return False + + def case_class(self, word): + self.packet.classType = word + if self.packet.classType in self.CLASS.values(): + self.state = State.COMMAND + else: + self.state = State.STX + return False + + def case_command(self, word): + self.packet.command = word + if (self.packet.len - self.HEADER_LEN) == 0: + self.state = State.ETX + elif (self.packet.len - self.HEADER_LEN) > self.maxLen: + self.state = State.STX + else: + self.state = State.DATA + return False + + def case_data(self, word): + self.packet.data.append(word) + if (len(self.packet.data) * 4) >= (self.packet.len - self.HEADER_LEN): + self.state = State.ETX + return False + + def case_etx(self, word): + ret = False + self.packet.etx = word + self.state = State.STX + if self.packet.etx == self.ETX: + ret = True + return ret + + def case_default(self): + self.state = State.STX + + def _recvThread(self): + while True: + recvData = self.sock.recv(16384) + print(f"[REcived] {recvData}") + self.recvCount += len(recvData) + self.dataQueue.put(recvData) + + def _parsingThread(self): + self.func1() + + def func1(self): + oldArray = bytearray() + while True: + newArray = self.dataQueue.get() + oldArray = oldArray + newArray + endIdx = len(oldArray) - len(oldArray) % 4 + dataArray = oldArray[:endIdx] + oldArray = oldArray[endIdx:] + + for i in range(0, endIdx, 4): + int_value = int.from_bytes(dataArray[i : i + 4], byteorder="little") + print(f"[IntValue] {int_value}") + if self.parsing(int_value) == True: + self.commandProc() + + def func2(self): + oldArray = bytearray() + while True: + newArray = self.dataQueue.get() + if len(newArray) % 4 != 0: + oldArray = oldArray + newArray + return 0 + else: + oldArray = newArray + + string_format = f"<{int(len(oldArray)/4)}I" + intArray = struct.unpack(string_format, oldArray) + for i in range(len(intArray)): + if self.parsing(intArray[i]) == True: + self.commandProc() + + def commandProc(self): + classType = self.packet.classType + command = self.packet.command + payload = self.packet.data + + if classType == self.CLASS["GET"]: + print("GET") + if self.classGetCallback != None: + self.classGetCallback(command, payload) + elif classType == self.CLASS["HEART_BIT"]: + print("HearBeat") + self._setHeartBit() + if self.classHeartBitCallback != None: + self.classHeartBitCallback() + elif classType == self.CLASS["DATA"]: + print("Data") + if self.classDataCallback != None: + self.classDataCallback(command, payload) + + def _setHeartBit(self): + args = (self.CLASS["HEART_BIT"], self.CMD["HEART_BIT"]) + print(self.CLASS["HEART_BIT"], "/" , self.CMD["HEART_BIT"]) + self.send(args) diff --git a/SDT_Device/PulseGenerator.py b/SDT_Device/PulseGenerator.py new file mode 100644 index 0000000..0d6c743 --- /dev/null +++ b/SDT_Device/PulseGenerator.py @@ -0,0 +1,237 @@ +from SDT_Device.Protocol import Protocol +import threading +import csv + + +class PulseGenerator(Protocol): + MAX_CHANNEL = 12 + + CMD = { + "SESSION_COMPLETE": 0x00, + "DEVICE_RESET": 0x10, + "DEVICE_START": 0x11, + "SESSION_PERIOD": 0x12, + "BASE_CHANNEL_ENABLE": 0x20, + "SESSION_COUNT": 0x30, + "BASE_CHANNEL_DATA_SIZE": 0x40, + "BASE_CHANNEL_SESSION_POSITION": 0x50, + "BASE_CHANNEL_OUTPUT_DELAY": 0x62, + "BASE_CHANNEL_DATA": 0x70, + "HEART_BIT": 0xF000, + } + + def __init__(self, ip, port): + super().__init__(ip, port) + + self.setDeviceId(self.DEVICE_ID["PG"]) + self.setDeviceSerial(0) + self.heartbitCount = 0 + + self.classGetCallback = self._classGetCallback + self.classHeartBitCallback = self._heartBitCallback + self.classDataCallback = self._dataCallback + + self._getEvent = threading.Event() + self._dataEvent = threading.Event() + + self._readBuffer = [] + + def _read(self): + self._getEvent.wait() + self._getEvent.clear() + return self._readBuffer + + def _classGetCallback(self, command, payload): + self._readBuffer = payload + self._getEvent.set() + + def _heartBitCallback(self): + self.heartbitCount += 1 + + def _dataCallback(self, command, payload): + if command == self.CMD["SESSION_COMPLETE"]: + self._dataEvent.set() + + def WaitUntilSessionIsCompleted(self): + self._dataEvent.wait() + self._dataEvent.clear() + + # enable = 0에서 reset 적용, 1에서 해제 + def setReset(self, enable): + args = (self.CLASS["SET"], self.CMD["DEVICE_RESET"], [enable]) + self.send(args) + + def getReset(self): + args = (self.CLASS["GET"], self.CMD["DEVICE_RESET"]) + self.send(args) + return self._read()[0] + + def setStart(self, enable): + args = (self.CLASS["SET"], self.CMD["DEVICE_START"], [enable]) + self.send(args) + + def getStart(self): + args = (self.CLASS["GET"], self.CMD["DEVICE_START"]) + self.send(args) + return self._read()[0] + + def setSessionPeriod(self, period, unit="raw"): + if unit != "raw": + if unit.lower() == "s": + period *= int(1e9) + elif unit.lower() == "ms": + period *= int(1e6) + elif unit.lower() == "us": + period *= int(1e3) + + if period < 5: + period = 5 + _round = round(period / 5) + else: + _round = period + + args = (self.CLASS["SET"], self.CMD["SESSION_PERIOD"], [_round]) + self.send(args) + + def getSessionPeriod(self, unit="raw"): + args = (self.CLASS["GET"], self.CMD["SESSION_PERIOD"]) + self.send(args) + + period = self._read()[0] + + if unit.lower() == "s": + period /= int(1e9) + elif unit.lower() == "ms": + period /= int(1e6) + elif unit.lower() == "us": + period /= int(1e3) + period *= 5 + + return period + + def setChannelEnable(self, ch, enable): + args = (self.CLASS["SET"], self.CMD["BASE_CHANNEL_ENABLE"] + ch, [enable]) + self.send(args) + + def getChannelEnable(self, ch): + args = (self.CLASS["GET"], self.CMD["BASE_CHANNEL_ENABLE"] + ch) + self.send(args) + return self._read()[0] + + def setSessionCount(self, count): + args = (self.CLASS["SET"], self.CMD["SESSION_COUNT"], [count]) + self.send(args) + + def getSessionCount(self): + args = (self.CLASS["GET"], self.CMD["SESSION_COUNT"]) + self.send(args) + return self._read()[0] + + def setChannelDataSize(self, ch, size): + if size >= 2048: + size = 2048 + args = (self.CLASS["SET"], self.CMD["BASE_CHANNEL_DATA_SIZE"] + ch, [size]) + self.send(args) + + def getChannelDataSize(self, ch): + args = (self.CLASS["GET"], self.CMD["BASE_CHANNEL_DATA_SIZE"] + ch) + self.send(args) + return self._read()[0] + + def setChannelSessionPosition(self, ch, position): + args = ( + self.CLASS["SET"], + self.CMD["BASE_CHANNEL_SESSION_POSITION"] + ch, + [position], + ) + self.send(args) + + def getChannelSessionPosition(self, ch): + args = (self.CLASS["GET"], self.CMD["BASE_CHANNEL_SESSION_POSITION"] + ch) + self.send(args) + return self._read()[0] + + def setChannelOutputDelay(self, ch, delayTime): + args = ( + self.CLASS["SET"], + self.CMD["BASE_CHANNEL_OUTPUT_DELAY"] + ch, + [delayTime], + ) + self.send(args) + + def getChannelOutputDelay(self, ch): + args = (self.CLASS["GET"], self.CMD["BASE_CHANNEL_OUTPUT_DELAY"] + ch) + self.send(args) + return self._read() + + def setChannelData(self, ch, data, unit="raw"): + rawData = [] + if len(data) > 0: + unitContant = 1 + if unit != "raw": + if unit.lower() == "s": + unitContant *= int(1e9) + elif unit.lower() == "ms": + unitContant *= int(1e6) + elif unit.lower() == "us": + unitContant *= int(1e3) + + for value in data: + value *= unitContant + + if unit != "raw": + if value <= 5: + value = 5 + _round = round(value / 5) - 1 + else: + _round = value + rawData.append(_round) + + args = ( + self.CLASS["SET"], + self.CMD["BASE_CHANNEL_DATA"] + ch, + rawData, + ) + self.send(args) + + def getChannelData(self, ch, unit="raw"): + args = (self.CLASS["GET"], self.CMD["BASE_CHANNEL_DATA"] + ch) + self.send(args) + + data_ns = [] + rawData = self._read() + + unit_const = 1 + + if unit.lower() == "s": + unit_const /= int(1e9) + elif unit.lower() == "ms": + unit_const /= int(1e6) + elif unit.lower() == "us": + unit_const /= int(1e3) + + unit_const *= 5 + + if len(rawData) > 0: + for value in rawData: + if unit.lower() != "raw": + nano_sec_data = (value + 1) * unit_const + else: + nano_sec_data = value + data_ns.append(round(nano_sec_data, 9)) + + return data_ns + + def readPulseDataFromCSV(self, ch, path): + ret = [] + + with open(path, newline="") as csvfile: + csvreader = csv.reader(csvfile) + next(csvreader) + for row in csvreader: + if row[ch] != "": + ret.append(int(row[ch])) + else: + continue + + return ret diff --git a/SDT_Device/__init__.py b/SDT_Device/__init__.py new file mode 100644 index 0000000..2a453a0 --- /dev/null +++ b/SDT_Device/__init__.py @@ -0,0 +1 @@ +version=1.0 \ No newline at end of file diff --git a/SDT_Device/__pycache__/CCU.cpython-311.pyc b/SDT_Device/__pycache__/CCU.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8974100aab942397a5a87343859029bdf80d2817 GIT binary patch literal 9031 zcmds7%WoUU8Qr7PwS|N9`tSZOceZofU$9^%XDPGu5@hahgd=>4TjXbW9`m-8 zZN_Hf&T_}{k`v5yg4`3(p0eVRETN?j?PI zPmzAWZV~|8M}mMoWB|~&#=Xza^a8Dex;-48{>lrKP#eHkJVF~n0O5Ir(+HCYmjRMo z5xC>h-ILSfQ&A~=VR|B_w}&r`O@zlH(TT8GB-1wEGosu&POjwlhKP~7b1FxS$T2ntl7~y6&t-6(_QDH@RH7q#S)eX zVSf(z!}3lMX!$LFhsz>OrF0kG_ss&gQPE;ma;yBR?Jh9mJ~D(Qo?GQMtdd!a?^W)q zwKn1RSxt9g=KI#{Y&`C(E)iRO&#R^q!|YO%IRsW}F6Sk|%x62z9LHTXRTEVGGkf*S zFqhlx`3Pe?=9qchO{-ZVj+dfRaw4EQ&;+s-L&lV@W$xlNmH zJM_HHCl!=@An;8%JTg12$W)nKxq2<0o}Z1#%C&4}Y4)AvcsjehC=vPHWI~=@TFEYC z(o!mUWp*k824K{JnH^eM`KIMmDyd`>nZ>17asjN9q+~jom83(bQkg_NrMvw5#1GuzA*+4V=SVJR{fMrlkt?^E+tZNML8>H z!|_z=N<48*Z>y{6Fgiuz*|^oKo%O1!a%C5&94E>2yzZKk7voC{87k|o(!8847xzd6 zrYV=*5{@~uJU0gfI4KBR*4@(5a!QfIndNjAcbG}06Gcow}jsH*YiTZCiJU9 ze<3iW1zuWnXo9~G9M*y-Fx&Sx|9;K?{DXH?|H-`nq~> z9sz zz5#+p76!J2fxIxJ2}7#+)Dw&AFk-)i3=qo$fsiv&Wth3ff)d@w*kQ3?6{7o9 zD_jv<4%~LaCY5V|_?kKG<(d7($K- zx(9kw3~v;pM5q}~CFdjb1*|xV@FK!7gu@7@5Pk$uEs+CIliORCNTm-5Ved5n5Q)3v zBl7XW`gLvZ!7cYe)qSwgzke;Fd3p<;Hr3M$nGHVg2^v{IcnYoFb;l=y8a%3XAKPj@ zrnVj{3_pj12H2p0<_THFW4s7KL&yMhObyBy?2*0Hy8M`ji3_1Ne4> z0?UA51m^K?XB_}3*noF^s%H9iJ2(RSDvvf-scMyOsFE^bE4z|a`<(r{1JCW)@=+qP)g?0?6X%cc84%?Pk z(47Rk=`Qvfhl7;vh|_sRLFWXnyX3s}Ryb~yXU(-Ed#kF@yX=es0Gqwt+uqQYH1$`Z13ZTlb>TOk=0TCF6Ra+Z|fjj7*fn^OC6II1C-;ptvpTJ#xS4CZ|xPF=D z#nbaJ!=}w3ZxZsAhh4q3O1BIxsH#G{+(CT?lElt!acD~%%8UCoasLL@!~?2upx#ed zQpITV=u(}tU59YLi%j`ineJv=)iw-47Om=R$*MLTHS(vZ=q8t#{80ktK?hlANW6+S zpa(yJmYw4O4JEFMFW1Q%xcFi2gWRpv+pDVJdvaYA9S5o@UPMbSY7*}SZzf500r*nG z-jaW!sDHZiJ?x~r*b5z$VQ>xnQ+Z0uKb5P{Z0R&Y5Fqitb}IyT@+N@kO3aed<0CwjB=b7aX8fLd>K@2oa>^ufCi7F6-a{}+nGcy$>WR_BZaP@D?hn5ADLZyH_| zkvFP%td1`Yv?b)nFG=b6qAW>zizF>($Z`tv9!Y|aaH&#@`~2uzQuy5H#6)!5_>p4> zw~9a5Q~ZsXMiIgYc=4q%gtrjbVHfYa6ayv2NK8`*?5aR9mqlP_A9NHnhj1O?X9&MQ zxCsDXmyAz42|1OLq!3R}Lal=F=Ctwsz=>TE@K1RFEnnXN%`yHJI=nYtD>iZ6y#;@; z@RSdq;BcXTu+TS9Xzwa?`HGz_?xrFKa3fso7M;N&*8m1=&fy|wfza$cP~%l{DH9>wD>}>HdJmcZZ-ipxq0gCuHa@uqv54PVzY$Iu x3j7NFH55@blsG`q&hvbMdq%B(3S66NKEI(n> literal 0 HcmV?d00001 diff --git a/SDT_Device/__pycache__/CCU.cpython-312.pyc b/SDT_Device/__pycache__/CCU.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d048cde9bc6d2bc304e623efea07594b5264d93 GIT binary patch literal 11000 zcmdT~Sx{To8NOEwTp_^)EMpe22)xN_;tlF#5`&~*8neWJolsI)))62qOXNs6f#Q~F z+y+`Fwr83eXWFsTWopI`^-O0vdGxe>>x*PNL35MNWTt)bn+r)N$y5LT+(q}AE5~tC zH@*X(&iT)B&-wo4EYjagOG`L>I!Z_OuH(3WvWx% z66Jjckd;`7mDq@#6p>;g5C_j}n4Hc1cz+zGX zD3D4(2U!DHLfn8(vNq}(v5=}WwxLq84kTry8c-xPfaRo?mDWLN1@VBSlGFpPAq{|T z(#T4ipmZ(qf~1Nx1Fj=2fYoF@U=7&-SW7kn){#ws9I{=%N(w$J+LY@H0dh#UThB@vverRJq=i4P&{rTS!_w!{KGRTy%WhMdO46ug&b?|xO6^4WSQDwA0} zMT&iH$)1{=h*C)ig<^?VIuw$esc1S7Jr#>YyNKin1dnuqL3aftQIq#aX)GL(Y`7_^;+6Qfbd8Jd`kr=tGkB(#H3C1Z(5j6@R=MdX~EfDV+5sAggb zxj_ku9-ka#X`fxPrK0f>$r788Y!gYEmiQQqM4m$^ltT8J;FTx zFpA;cL^MESr=r9Cfq~%(njDWt(!&!|>9J%Y6ptN8aY&|h7$VP(i7ASfFzv#O$~~+W zT>fSsz#VztZJ}dP>O}Ys{vVy@bTImy*E{-Hmo(w05R6G%$iYLP) zg)aw9(!E&U3oyNwsp4J>-U3CbT7X$DSJy00S#X^zn=e}uYO_M^`+{c$O1X#<_At$9 zv->e*V0PR%6B}V%ADES*{Co$t zbsZWfi{0(BG^q?W~?(j>d@S9vRQ@Zx98b_mvI+ zVBKB4ST?KdvX2g!wycj6K zNiws<82n{sIm3=VYQ*B{^U!9|B2T6V(kbjs*=FZ4hCLl_~%wK&{V;>u-r0 zuh6WxB_nKE9i24w%|fGxSKo-`G_J=cdyE-AuRL?dQ5_Tx*XT5ziqkCG?olyqhasxe zFcs^lVZ6yoH4p+y#i}c~Su3d2jQEsJrDsJ5K-{Y7}{JzGVFbn|og*k)b1oW4}&9T|aqR=yz5qnqDV_@cM zMGYLo>Znm=ZfLR%n3qwUjBe|&Ju7a%CGNa#$%?x(!tTcl*nMX?$YS_>DQv? zn^>U;yV`+GC|t9!b&Ao4x->T@$qiOifHSj_A0lV_0nAMcIm?KC9bY=rT4|<#tj30AD z5g@GS_)v4_k&nIi<0B*P)p22F9T-Q{M~f+MfrL4b{!F3NQV;Eq{8mj~+Nxm~%K4Vv z;YMlqjis+lW3Z<%+l-X_b}X`LA{pwof6Oa2-3W!MX9(u?P(EJ(kz-lfGG+?wsDpnrm3T+N2V0w_wk5mh@dC|Mr z>~T6sqZ63#O(;^J9i;P91g-rJ8y!P-aXltr`|hiJQb+>64OQZk$LtnP|Je|7|9D|&m_ zU^a-*iEtP|vc>Tl`3UBR5S~ZCqYA|vCpwN0L3jlrg)oVLCv`f7@B;)qI?-8#*Aae# zfV)6?4&ed`DFmc~m0ByLe& zMi`S{2v5tl^9=nx6yc^ZB<$3-e+~yCt@((%ZCq*d>@0r{DDct<>@>~B4z2O?V literal 0 HcmV?d00001 diff --git a/SDT_Device/__pycache__/DTS.cpython-311.pyc b/SDT_Device/__pycache__/DTS.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3517acae59aa32b8cfa5cb6d941213395236192f GIT binary patch literal 8033 zcmds6&2JmW72jQw(vqS?eVf$xhn-lqYscS;eD z0iC1{&_(J2t4IT&n=}GelP16#(hTS!2LNk{7qE`_0P9H$U;}9dY$R=fO{5*LnREai zAf15TG~I z;GghI1O8A@b%$<;CW52VcrX;4P+fuGz^x&68R~vABn8L(10z8x5FGK})-)mi=uOa! z`^ScYs)%)<{Y}-WN&fK(wFbqB;nCnY;NW%Dc`XQPX<&FFVk{Dj3CEA+ojlKRzvUlu zNjU4rGZ0?r1@5WoilO9I`BmW=5by#?U&KlER%U-_Q(YxMdv zI681J?O-Le7k02xT}i3Fq|~67U{*#ekc~DV8!bRK>d)BWu8n$YB0#ChC~;uKwlRwI zF$3;vRvn3@MVYFiBt_%Vq$H_T2{{>%m!lDRn5fP`@RQ-epfns%J%&Cc(`Y!R)=80A zIFT5VlY`+{>`pjxSFN>Z>a|LNa58Mvs$;cERLxqoYjT(-2cpFen^;wg+L@fCa+pNp zGpZxFEXR|oQ<{;J!R5G|R9zB*yJ>Qjg!KlNrl(W7tUm9IqamgrrtU!TZ`Jz23w5qn;X?t-@QNyl?X)^|K3p2&(P6!An3FSTunZR;Or z#LlePsfeAqu7laGb7^~4Y|nKc%69jo*pWNbmpya^#h$^4KV^f&imLcLPZ~z%7j#l@E@k z!3Ou(5Da^)&qm*cF6bcu)dqgIVPjKKD6;LXA4wCS}&A=Olz zp2;1hrb1H0JN+J})2C>cX)3#qi za4Elgtn#~4W9E4FG1Uf-6ckfW=6D-6)iF5Y4}~b27^X0BvQaPvdzj87N{pcf_M6a3 zA}P<0%OIxek)TrD7<7}s9R(<=ZG7ZfKbx)XetPEj{m;j;y&r6bH$#fYpSD9#{mS{J z^Xr2TJ!_sc`?KyW21k#ezc=S-T6hzFfJwZc_4SFlg*!)Y3NemgkT z9y~%Y9*U1MbOOP`m>bv$$aj`zFtTf|Y1VuSjS}YpENqQrd*4?)7xrTdqSZbdxivt2 z9}k5MRhSXLM{RrhsI9Dz!b8|~WB?!M=m-GV6egc^2tc(hhhy|6NcuB{mfxv$;D-L? znP5D8Cng8vSa?Ot_AKftXuER;U^h|9u}j(B%P)IgMqc_9&vgr5=%v;z%J9*3=74klqHVyv>Ee^NTTg9G+%nv(1^*GWLRo_y2A` zj1I$o=+BXpayDa{yGDs-GbVKu+4#($1{@Uzo^osM(gI2-DKDmK3fn(836|r~ zM1PJd-TFVu_MYGD*^F#LU>UUd!H*%ZFwXWf3|GMd4Bm=mio9VKx;uP4FYpG@2Ex)z&77?6f-e-A^}xG5?dig#_ySs&x2Nmb z-u}%~o1bsKt9S-1%={SasmL75hRQIf+mFGVN%0G0uB=5Bm^-L=E?aop%c9<<*wlm; zwO4F%gYis6=Vdw(joj5zM6>2TvQj?txtQ(!@TKr_aC1fR3|ol#7Y4Nk{k}b!k13mG zWbI);m`?BF%B*>STwMg%9g~z}8dsZ1#S^eFWilZ>r|xUP4+!z_ybK=@T#_`uK$c=C zyCv!KrEsiprE1jwQ}_tx9~%pfXirc0pvB(GQhax*JxAeN9(oM{AJFN?2)7V0{ii=e zkPz-55QJ$2e8#4jLD3ijZd7y;K&>vkqL0Y2m?Xi=;gg_cFAJ||uTAmo7W*cGLgLr( z1P|}o^}k$w!~N^|3a-67=WWY1x8&No@Ym?gHTiNa9l55~e1og1BF_QbAIy8b4qu)t z17~xW8keoUEnHgQJjH<_Jgl6gSOJ@I2;) zlrSN%UvWZYzm^G$zzuUmyvPxY!rv10_7m2u;J4nxf}huhaEa_9L8UySqjYGQaicFzLirUJk{4?-`(K7{=U2M`V-90Evk_kjzW<_U&F za_E>G4vq)qp%>2{eqpL8E0;H55U^=sSZ0XX#9PIk)k9tCWQnuCnxN zw{VwROkt14NLdM7QBSPYR8eZKC>i4?0z9R)T$g#eoFkSpXJPJ&m=LD8gzyfm_62aO zYr^|=7ARj3q3w=xi!4ZZCViLlTcZ@0nVEL71&KRFh;mIg`xDvgS~g zOjtRWOeiBnvxOsXjD#ZcNLX_l@|Z%C@swr*!D#&a31voWloP4AstzleP&}176;GVi zJX<79x>7ivi5pstOshiFrmG!O;xsdq+<0IsQ{5uAWlmEiPLeZInl*AxnaOB2c}mGd z&dn$p%`Ow@P0u@I)Ek95;F(p_8&O3@8G_D z`vwDV&eF4LI-XF}0LiFNr?XGT)9FAgJTB|Y5P-Nekj_#x;hYl-<+W-9iTY=NFBZ7Z zq=u#Xg5+IkS{c71_1%@6YmyhsEgy7W@4jIz_y+Dsfg*}K3sUFGYj>m`C|z+axvsu< zM|z^zyKAlY*+TELiz=7GzhB(Xlh3Gwe zl%J>QP&avsFMW@B`ZafJtnn*$H(AgSr5>FZ^DH40>#P1- zuqxn%uRcrt6-tC0sGo%er9I3(3_FKb_@qsV2xLJ=5h!K9svmJvKgNh_ z^dmlGKbB4X7;~u6kL4lzv2N@ompuiS#h!?xAhpLyG)$niv`l zQ$NLe0FNWWLwdnL*i7rJ~_J8c``mnL!?7QRaDmJu04EJ95=KBs6e9x@%1>bWY zH9Y?fx43WRTrcQeV2)=-bodp_cA1;vE(r5H;hCkoV2wb!Ie~@{13M;yp=EwzN!`#A z4K2gQHlAo`S(ug+daM#PbS>NxVMlD~X#{f^upySPRj*udh`#v7(1gTkO{7YuV(m4@ z7(|kwzN5&w(FRO{W6(~W0$AYgx;%^GT}Q*!#!H8bO>N78Yk|ekeGlhpUbNqLb1u)- z{g>X{sBr2Pjx80EUQxff;;y@C(Y{T{PQfVte<3?t3eF<^7W7Ml2tx>)gR)b`xLxMY z!+gjjc@x^GeE@ohb}za0EewWa2+^$ERgk(qDTievm_i%$X8e0F2~cb9dQb*qsbYAf z6n3ZhN$~;(btB}!150V1FYnFqW$m{_H4)Bz6i)`sx^9Y^HFP2vi_u{a;eK(3+!S<+ zvc&0>s$$2bH@4=2mwPO$W|ZkM1+uv&B&1UI9Z*+sCj*ptT9)nC>?_X{JUc%;^t-`d z58j9t`d?U$ug3E3;GzX7vMt%J?z-&WY8eJQKZ0jHJUf%Z*I@>@GtP9-T`84c@kGSe z8yv)uz2P!@$jeO&eGPsXcXSMMA%qw}C2z3p-@;p^4EJFwZfR=rmUka9aRxhBck`9p zQtl`7E5W57?ORQ(LdXc2Z2oZ!8H}Or zjKE%6ZG)wa@(++DJYH?b62yh0CXOEcxPaHYHEjlOT$zo)ji-KmFbOUqA2qG!c%grA z^}wpS>d(8wCPp4dt6>~%XElzpl^8sk1dH3+O^K&ndG|{umL7e(!4Cm7?ZydaGM=1P z=y)=5R^P~(C6|$r+Iy5Eh5i?B3%5h7*}QwiM9Sl6I*g|6Ob7S8o2W8Nt|3+Ud9+&4 z$?w&vTFq4YSPkp(x8ui@nfSBNF%WQL8 z;{O(JvDH^>?=H4>{Il8as9Okq?rpIK*7x$(_Vqf!+O;l;*4}ly)7rn@+hA>8?-rma z305yw^sL)OYv;OsqxhA%e`7PYCt^c{Te{|r&m;roFVYmZ3Un8MW=YJZvszsSoFtiH zZEos&DmK|Kr6c;{9HsApHeMU3JplJbp6CC@?fI1Re9AdKUiA}Nac{!HvDiQ|N>BgwL-PFsx|iezLp661)pld#jlz+8x;4Sr++ zsg){EXKKx)mmG5K^k5xz>7jo~&wK4jx85?7Q-5zkQXoa^WP^j>d%Ji5e)#Rf+XWww zj3gMgo7b-Imd6?UFEzrOiN#knbl{LzSvS!)bVBFy53FD@t zy)?=Zt`}K4a*nfET9dIyqUkp)O}9{?p75r4pu_tQz+q*LGhAK6m36uzkMJlT;xQiQ z2|oPKc-j1(?P@&9M^0k6ONx)4uyT}-@p0H8KEWqpV|%dbD)P=^uwWW&lAGV5D;E0^EO zd>ix>Guw~Qvy&-o;5~~TGiwNOG1xe40(RI> zz8X2j8CI(6jaI2Wrf^*0gu+RMQyllQrYSW}m_Ng#FSQ(dsiAL{Ge|yE5JXeSU7G^! z3JKrYbU1Xlh~1;Xplkjy_5x}5qyC+V^EGwm8B6O@Us{oxC8iLj;OEuly=L86_MA#% zb^p%ly_LHwD=VKaf87+%y_Q{Zyyar9WaXTLTE$t$z%R89Wn#^(d48o?Z#}q0HzggT zOlL=n6ku7kM$NaZr8T!%v0d*0oKCo)(Da;)c>66!$XF+~JnT7saFb15j_2}EHnVwa zGglt%_7@#dv)ytW9ozPwx15F?w<@mfc^i(OwO#kAU3p#}>+g{hK~T>2?QYb#iVAnh znIP<;V+()1c5%UJ724k|b8Wo-Ct`qb$o}_a*?x$irtS zqxUiJ7K$kHOfQ^M81n5Sm@`dKc3k9|P=v0b?>2Ui@|I~w3+k2wWxn*n(kY4nQvv<;lCRuqMchZVoZ zb;WN0NE7v(y^o08rdLYpl|5+)$M1VuIlP0M$OJ{^Ap$*mPnd4%>U9rZdSFyP8l@)= zMZZ3$NjQ4E{+y=a3NqRd?;%F~ir_xMpiE9$l<;kCUn8phmL;^KF{OU!a1R;MoV3r< zIW*2hhNfj$s7*Os)pm^+$ZAa48cob9lgxrNu&59tI7MaBnIR+DtxT~fev2-upACMd z2ZEZmsCxZsrcgYJho0}$cO1BXAOiG$F>Ovenjq4>0k5Y`gVs-bR}WJLdE*q1GEa4s zsBd(rQ$;;9#X5{4E=UMLIW2Mo-KkuaSDC^z6e4XQq+L3uP&@+Ky&E;>9RI1^_kH#Iy!q8V9y3@^q+A$Z-sA=AfEy*iT(2=eYJ^4N>g`SZL1;mtroJ zjBe^K;q1$tRqA3Q1LMx>GMop2sERoiL#1qcJ`+TLFms65%e+p^qX0@>WPzI3E=9dz zy=f_`^cG)`H(1`HU2nP04lQp4q6W$vig_#YDxzI^MPU%zK&%jx17bT^(oobJmK5K$ zS4f%&va#qpt-thWnQGT6Bn2IODobu)J(Mz7 z`k(aZrY6bI(YGR;B6qcL!yRS7@b7)fDUPAzZ&A}vqUmM`yGOT;X{MoTeZQZg$>dDTh?%iSY%DekOI}1F zdEH(8=a7z;Cold0SnB-gC1_P9t^=f5X|@i_Q6Fhg^Mn5>E=s-fh?tD61yVks^^6w- JB!l&+@qZwd^2h)H literal 0 HcmV?d00001 diff --git a/SDT_Device/__pycache__/Protocol.cpython-311.pyc b/SDT_Device/__pycache__/Protocol.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6610788efd929ec2ec54457981bbaa1ad6fa9612 GIT binary patch literal 6090 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 literal 0 HcmV?d00001 diff --git a/SDT_Device/__pycache__/Protocol.cpython-38.pyc b/SDT_Device/__pycache__/Protocol.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..388e5bc96114e5a71d3b38a0789bc60a789d2ba3 GIT binary patch literal 7074 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 E7bc&Dn*aa+ literal 0 HcmV?d00001 diff --git a/SDT_Device/__pycache__/PulseGenerator.cpython-311.pyc b/SDT_Device/__pycache__/PulseGenerator.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a9dbb917ac95875e3d7a579980c76cf6802fc422 GIT binary patch literal 10105 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 literal 0 HcmV?d00001 diff --git a/client.py b/client.py new file mode 100644 index 0000000..d3982e9 --- /dev/null +++ b/client.py @@ -0,0 +1,48 @@ +import struct +import socket + +# 서버의 호스트와 포트 설정 +SERVER_HOST = '192.168.0.20' +SERVER_PORT = 5001 +HEART_BIT = 0x48 +HEART_BIT_CMD = 0xF000 +HEADER_LEN = 20 +STX = 0x2 +ETX = 0x3 + +# 소켓 생성 +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + # 서버에 연결 + s.connect((SERVER_HOST, SERVER_PORT)) + + while True: + # # 서버로부터 데이터 수신 + data = s.recv(1024) + print('수신된 데이터:', data) + + data = [] + length = HEADER_LEN + payload = [] + deviceId = 5 + deviceSerial = 0 + classType = HEART_BIT + cmd = HEART_BIT_CMD + + if data is not None: + payload = data + length = HEADER_LEN + 4 * len(payload) + + string_format = f"<{7+len(payload)}I" + # 서버에게 메시지 전송 + s.sendall(struct.pack( + string_format, + STX, + length, + deviceId, + deviceSerial, + classType, + cmd, + *payload, + ETX, + )) + print("전송 완료!!") \ No newline at end of file diff --git a/framework.yaml b/framework.yaml new file mode 100644 index 0000000..793d4f5 --- /dev/null +++ b/framework.yaml @@ -0,0 +1,12 @@ +version: bwc/v2 # bwc 버전 정보입니다. +spec: + appName: dts-py-app # 앱의 이름입니다. + runFile: main.py # 앱의 실행 파일입니다. + env: + bin: python3 # 앱을 실행할 바이너라 파일 종류입니다.(장비에 따라 다르므로 확인 후 정의해야 합니다.) + virtualEnv: dts-env # 사용할 가상환경 이름입니다. + runtime: python3.9 + package: requirements.txt # 설치할 Python 패키지 정보 파일입니다.(기본 값은 requirement.txt 입니다.) +stackbase: + tagName: v1.0.0 # Stackbase(gitea)에 릴리즈 태그명 입니다. + repoName: dts-py-app # Stackbase(gitea)에 저장될 저장소 이릅니다. diff --git a/main.py b/main.py new file mode 100644 index 0000000..992e3fa --- /dev/null +++ b/main.py @@ -0,0 +1,124 @@ +from SDT_Device.DTS import DTS +import sys + +from PyQt6 import QtCore +from PyQt6.QtCore import Qt +from PyQt6.QtWidgets import QApplication, QMainWindow +from MainWindow import Ui_MainWindow +from pyqtgraph import PlotWidget, plot +import pyqtgraph as pg +from random import randint +import threading +import time +import struct + + +class MainWindow(QMainWindow, Ui_MainWindow): + def __init__(self): + super().__init__() + self.setupUi(self) + self.show() + + self.pb_Start1.clicked.connect(self.SendStartPacket1) + self.pb_Stop1.clicked.connect(self.SendStopPacket1) + + self.pushButton_EnableDelaySet.clicked.connect(self.SetEnableDelay) + self.pushButton_AverageStepSet.clicked.connect(self.SetAverage) + self.pushButton_SampleRangeSet.clicked.connect(self.SetSampleRange) + + self.comboboxMinVal = 8 + for i in range(self.comboboxMinVal, 12): + self.comboBox_AverageStepVal.addItem(str(2**i)) + + ip = "192.168.0.20" + port = 5001 + self.dts = DTS(ip, port) + self.dtsInit() + + self.x1 = [] + self.y1 = [] + self.data_line1 = self.graphWidget1.plot(self.x1, self.y1) + + self.timer = QtCore.QTimer() + self.timer.setInterval(250) + self.timer.timeout.connect(self.update_plot_data) + self.timer.start() + + self.tick = 0 + + self.prevDataCount = 0 + + def SetEnableDelay(self): + val = int(self.lineEdit_EnableDelayValue.text()) + self.dts.setAvgEnableDelay(0, 10 + val) + self.dts.setAvgStart(0, 0) + self.dts.setAvgStart(0, 1) + + def SetAverage(self): + val = self.comboBox_AverageStepVal.currentIndex() + self.comboboxMinVal + self.dts.setAvgStep(0, val) + self.dts.setAvgStart(0, 0) + self.dts.setAvgStart(0, 1) + + def SetSampleRange(self): + val = int(self.lineEdit_SampleRangeValue.text()) + self.dts.setAvgSampleRange(0, val) + self.dts.setAvgStart(0, 0) + self.dts.setAvgStart(0, 1) + + def SendStartPacket1(self): + print("Send Start1") + self.dts.setAvgStart(0, 1) + + def SendStopPacket1(self): + print("Send Stop1") + self.dts.setAvgStart(0, 0) + + def update_plot_data(self): + self.y1 = self.dts.ReadAdcData(0) + size = len(self.y1) + self.x1 = list(range(size)) + self.data_line1.setData(self.x1, self.y1) + + print(self.dts.dataCount) + if self.prevDataCount != self.dts.dataCount[0]: + self.prevDataCount = self.dts.dataCount[0] + current_time = time.time() + formatted_time = time.strftime("%H:%M:%S", time.localtime(current_time)) + print(f"[{formatted_time}] dataCount={self.dts.dataCount[0]}") + + debugData = self.dts.getDebugData() + self.lineEdit_TriggerCount.setText(str(debugData[4])) + + def dtsInit(self): + self.dts.setSystemReset() + + self.comboBox_AverageStepVal.setCurrentText(f"{self.comboboxMinVal}") + avgStepVal = self.comboBox_AverageStepVal.currentIndex() + self.comboboxMinVal + + self.lineEdit_SampleRangeValue.setText("50000") + sampleRangeVal = int(self.lineEdit_SampleRangeValue.text()) + + self.lineEdit_EnableDelayValue.setText("0") + enableDelayVal = int(self.lineEdit_EnableDelayValue.text()) + + ch = 0 + self.dts.setAvgEnableDelay(ch, 10 + enableDelayVal) + self.dts.setAvgSampleRange(ch, sampleRangeVal) + self.dts.setAvgStep(ch, avgStepVal) + self.dts.setAvgStart(ch, 0) + + self.dts.setTimerTick(ch, 100_000_000) + + +def mainInit(): + app = QApplication(sys.argv) + w = MainWindow() + app.exec() + + +if __name__ == "__main__": + mainInit() + + +# pyuic6 .\MainWindow.ui -o MainWindow.py diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b629c5e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +# Write package's name that need your app. +awsiotsdk +pyyaml +pyqt6 +pyqtgraph +#sdtcloudpubsub diff --git a/server.py b/server.py new file mode 100644 index 0000000..7556766 --- /dev/null +++ b/server.py @@ -0,0 +1,26 @@ +import socket + +# 호스트와 포트 설정 +HOST = '192.168.1.45' +PORT = 8088 + +print(f"TEST: {[0, 100_000_000]}") +# 소켓 생성 +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + # 소켓을 주소와 연결 + s.bind((HOST, PORT)) + # 연결 대기 + s.listen() + + print('서버가 시작되었습니다. 클라이언트 연결을 기다립니다...') + + # 클라이언트로부터 연결 수락 + conn, addr = s.accept() + with conn: + print('클라이언트와 연결됨:', addr) + # 클라이언트에게 메시지 전송 + # conn.sendall(b'안녕하세요, 클라이언트!') + + # 클라이언트로부터 데이터 수신 + data = conn.recv(16384) + print('수신된 데이터:', data) \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..13c2fa8 --- /dev/null +++ b/test.py @@ -0,0 +1,55 @@ +from SDT_Device.DTS import DTS + +import time +import sys +import json +import sdtcloudpubsub + + +# with open("./info.json","r") as f: +# info = f.read() + +# ch = info['ch'] +# ch = 0 +# ip = info['ip'] +# port = info['port'] +# enableDelayVal = info['enableDelayVal'] +# sampleRangeVal = info['sampleRangeVal'] +# avgStepVal = info['avgStepVal'] + +# Default Setup for Test +ch = 0 +ip= "192.168.0.20" +port = 5001 +enableDelayVal = 10 +sampleRangeVal = 100 +avgStepVal = 10 + +sdtcloud = sdtcloudpubsub.sdtcloudpubsub() +mqttClient = sdtcloud.setClient(f"device-app-{uuid.uuid1()}") # parameter is client ID(string) + +# Run Start +dts = DTS(ip,port) +dts.setSystemReset() +dts.setAvgStart(ch, 0) + +dts.setAvgEnableDelay(ch, 10 + enableDelayVal) +dts.setAvgSampleRange(ch, sampleRangeVal) +dts.setAvgStep(ch, avgStepVal) + +# Get Data Start +dts. setAvgStart(ch, 1) + +dts.setTimerTick(ch, 100_000_000) + + +while(1): + print("Run") + y1 = dts.ReadAdcData(0) + + print(y1) + msg = { + "sensor_data": y1 + } + sdtcloud.pubMessage(mqttClient, msg) + time.sleep(1)