Browse Source

Added 7bit encoding controls
Added error handling
Added icon

Timo Dritschler 3 years ago
parent
commit
d7714a1520
3 changed files with 196 additions and 36 deletions
  1. BIN
      KCG/icons/bbb.png
  2. 84 0
      KCG/icons/bbb.svg
  3. 112 36
      KCG/widgets/FrequencyExtractWidget.py

BIN
KCG/icons/bbb.png


+ 84 - 0
KCG/icons/bbb.svg

@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
+   sodipodi:docname="bbb.svg"
+   width="156.05023mm"
+   height="156.05023mm"
+   viewBox="0 0 156.05023 156.05023"
+   version="1.1"
+   id="svg8">
+  <sodipodi:namedview
+     inkscape:current-layer="g854"
+     inkscape:window-maximized="0"
+     inkscape:window-y="0"
+     inkscape:window-x="0"
+     inkscape:cy="294.89808"
+     inkscape:cx="222.44781"
+     inkscape:zoom="1.3733559"
+     showgrid="false"
+     id="namedview15"
+     inkscape:window-height="1200"
+     inkscape:window-width="1920"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0"
+     guidetolerance="10"
+     gridtolerance="10"
+     objecttolerance="10"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff" />
+  <defs
+     id="defs2">
+    <rect
+       x="34.924088"
+       y="120.61496"
+       width="181.0349"
+       height="55.423088"
+       id="rect837" />
+  </defs>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     transform="translate(-23.92816,-28.583433)">
+    <rect
+       style="fill:none;fill-opacity:1;stroke:none;stroke-width:1.27808;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+       id="rect833"
+       width="156.05023"
+       height="156.05023"
+       x="23.92816"
+       y="28.583433" />
+    <g
+       id="g854">
+      <text
+         transform="translate(-11.337337,17.344064)"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:70.5556px;line-height:43.9px;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro';letter-spacing:-4.82335px;word-spacing:0px;white-space:pre;shape-inside:url(#rect837);fill:#000000;fill-opacity:1;stroke:none;"
+         id="text835"
+         xml:space="preserve"><tspan
+           x="34.923828"
+           y="165.06366"><tspan
+             style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:70.5556px;font-family:Inconsolata;-inkscape-font-specification:'Inconsolata Bold'">B B B</tspan></tspan></text>
+      <path
+         id="path843"
+         d="m 31.542704,34.03342 c 3.311542,19.029618 5.102862,60.30759 11.350151,74.51742 7.661991,17.42765 17.067664,6.51039 21.128482,0.61201 7.533542,-10.942557 8.872452,-22.513992 10.454304,-19.833716 4.238106,7.181012 14.20453,37.230906 18.918204,9.720169 13.525175,-78.937877 9.793695,-79.066545 19.772125,-0.335035 0.82334,6.496312 14.33195,34.855322 28.86962,-4.179684 2.62496,-7.048282 0,0 8.80041,15.035096 2.94906,5.03834 10.71385,7.25621 21.57097,6.61258"
+         style="fill:none;stroke:#000000;stroke-width:7.965;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>

+ 112 - 36
KCG/widgets/FrequencyExtractWidget.py

@@ -11,10 +11,7 @@ import pyqtgraph as pg
 from math import ceil
 import numpy as np
 import socket
-<<<<<<< HEAD
 import sys
-=======
->>>>>>> da4c26e... Added ethernet socket to FrequencyExtractWidget
 
 
 
@@ -105,45 +102,122 @@ class FrequencyExtractWidget(kcgw.KCGWidgets):
         self.port = self.createSpinbox(1024, 65535)
         self.port.setValue(56000)
         self.ethernetControls.addWidget(self.port)
-        self.ethButton = self.createButton("Connect", connect=self.connectEthernet)
+        self.ethButton = self.createButton("Connect", connect=self.connectButtonClicked)
         self.ethernetControls.addWidget(self.ethButton)
 
         self.socketConnected = False
 
 
+        #7-Bit encoding values
+        self.encodingControls = QtGui.QHBoxLayout()
+
+        self.encodeBase = self.createSpinbox(0, 10000, interval=1, connect=self.updateRange)
+        self.encodingControls.addWidget(self.createLabel("Encoding Offset:"))
+        self.encodingControls.addWidget(self.encodeBase)
+        self.encodeStep = QtGui.QDoubleSpinBox()
+        self.encodeStep.setDecimals(2)
+        self.encodeStep.setMaximum(1000.)
+        self.encodeStep.setMinimum(0.)
+        self.encodeStep.setSingleStep(0.01)
+
+        self.encodeStep.valueChanged.connect(self.updateRange)
+        self.encodingControls.addWidget(self.createLabel("Encoding Step:"))
+        self.encodingControls.addWidget(self.encodeStep)
+        self.encodingRange = self.createLabel("NaN")
+        self.encodingControls.addWidget(self.createLabel("Valid Range:"))
+        self.encodingControls.addWidget(self.encodingRange)
+
+        self.encodeBase.setValue(10)
+        self.encodeStep.setValue(0.08)
+
+
+
         self.layout.addLayout(self.plotBox)
         self.layout.addLayout(self.controlsBox)
         self.layout.addLayout(self.frequencyTools)
         self.layout.addLayout(self.ethernetControls)
+        self.layout.addWidget(self.createLabel("7-Bit Encoding controls (kHz):"))
+        self.layout.addLayout(self.encodingControls)
         self.setLayout(self.layout)
         self.setWindowTitle("Frequency Extract")
 
 
+    def connectButtonClicked(self):
+        if not self.socketConnected:
+            self.connectEthernet()
+        else:
+            self.disconnectEthernet()
+
+
     def connectEthernet(self):
+        if self.socketConnected:
+            return
+
+        #closing a socket also frees the underlying File-Descriptor,
+        #so we need to create a new socket every time we want to create
+        #a new connection
+        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+
+        try:
+            socket.inet_aton(self.ip.text())
+        except OSError:
+            print("Malformed IP: %s"%self.ip.text())
+            return
+
+        try:
+            self.socket.connect((self.ip.text(), self.port.value()))
+            self.socketConnected = True
+            self.ethButton.setText("Disconnect")
+        except:
+            print("Failed to connect to %s"%self.ip.text())
+            self.socket = None
+
+
+    def closeSocket(self):
+        try:
+            self.socket.close()
+        except:
+            pass
+
+        self.socketConnected = False
+        self.ethButton.setText("Connect")
+
+
+    def disconnectEthernet(self):
+        if not self.socketConnected:
+            return
+
+        #We tell the other end that we want to close the connection
+        #by sending 0xFF. Our values are usually 7-Bit encoded, meaning
+        #0xFF is an "Invalid" value and can be used for signalling
+        self.sendValue(0xFF)
+        self.closeSocket()
+
+
+
+    def sendValue(self, value):
         if not self.socketConnected:
+            return
+
+        #socket.sendall() has two different behaviours, based on the Python
+        #version.
+        #In Python2, socket.sendall() will only accept a string
+        #But in Python3, socket.sendall() will only accept a byte-sequence
 
-            #closing the socket also frees the underlying File-Descriptor,
-            #so we need to create a new socket every time we want to create
-            #a new connection
-            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
-            try:
-                socket.inet_aton(self.ip.text())
-            except OSError:
-                print("Malformed IP: %s"%self.ip.text())
-                return
-
-            try:
-                self.socket.connect((self.ip.text(), self.port.value()))
-                self.socketConnected = True
-                self.ethButton.setText("Disconnect")
-            except:
-                print("Failed to connect to %s"%self.ip.text())
-                self.socket = None
+        toSend = None
+        if sys.version_info[0] == 2:
+            toSend = chr(value)
         else:
-            self.socket.close()
-            self.socketConnected = False
-            self.ethButton.setText("Connect")
+            #bytes converts an array of integers into their shortest possible
+            #bytewise representation.
+            toSend = bytes([value])
+         
+        try:
+            self.socket.sendall(toSend)
+        except:
+            print("Failed to send... closing down connection")
+            self.closeSocket()
+
 
     def sendFreqEth(self, freq):
         if not self.socketConnected:
@@ -152,8 +226,8 @@ class FrequencyExtractWidget(kcgw.KCGWidgets):
         #We work in the kHz domain
         freq /= 1000
 
-        offset = 10
-        step = 0.08
+        offset = self.encodeBase.value()
+        step = self.encodeStep.value()
         max = (offset + (step * 127))
         
         if (freq > max) or (freq < offset):
@@ -166,14 +240,13 @@ class FrequencyExtractWidget(kcgw.KCGWidgets):
            tmp -= step
            count += 1
 
-        #bytes converts an array of integers into their shortest possible
-        #bytewise representation.
-        try:
-            self.socket.sendall(bytes([count]))
-        except:
-            print("Failed to send... closing down connection")
-            self.socket.close()
-            self.socketConnected = False
+        self.sendValue(count)
+
+
+    def updateRange(self):
+        rangeMin = self.encodeBase.value()
+        rangeMax = rangeMin + (self.encodeStep.value() * 127)
+        self.encodingRange.setText("%.2f - %.2f (kHz)"%(rangeMin, rangeMax))
 
 
     def dataSetChanged(self, data=None):
@@ -269,6 +342,9 @@ class FrequencyExtractWidget(kcgw.KCGWidgets):
 
         self.board_config.unobserve(self, 'lastDataSet')
 
+        #Tell the receiving end that we want to terminate the connection.
+        self.sendValue(0xFF)
+
 
 def addFrequencyExtractWidget():
     global __widget_id__
@@ -280,4 +356,4 @@ def addFrequencyExtractWidget():
         w = FrequencyExtractWidget(nid, global_objects.get_global('area'))
         global_objects.get_global('area').newWidget(w, "Frequency Extract", nid, widget_type=4)
 
-kcgw.register_widget(QtGui.QIcon(config.icon_path("sproject.svg")), "Frequency Extract", addFrequencyExtractWidget, "Ctrl+e")
+kcgw.register_widget(QtGui.QIcon(config.icon_path("bbb.png")), "Frequency Extract", addFrequencyExtractWidget, "Ctrl+e")