prologix_gpib 0.4.4 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ce983416d3fa34a8a34428381d564403c2f48664e153172066bebe1fe619f24
4
- data.tar.gz: 57abf8c3a078fb360684782852b9dfda21815519552ea3cba0ae90babc50bb6c
3
+ metadata.gz: 7ac431f2d4186660651615941aa70b69de9d49de98d8101861e66daf9cc90cd9
4
+ data.tar.gz: c77f6db86bf82377345283cfc41e62b4cfe9f4d37f5ba5375021a4711f696369
5
5
  SHA512:
6
- metadata.gz: dce9ffeab0971eade1b9e2b7d889b082e91580ccabaecd7ad39ae83bc502d2c3973e3ce3e7509832e7a207eb8d1a5f10aaf2a44a5a31e805a21198b07b045925
7
- data.tar.gz: a3b93b86db7d811de048a3cb38d56cf4a7c6a2e07ff51ef2975a9ceb978511275f3568a51d8b63e104ff4ace5bd8e64b26428fa95a2e52ea0587a269b7edd01d
6
+ metadata.gz: be989bbe8e07016b4f42b0b865474338fce70eaed5e828dae502d91db0d637903274bb9c734ac81e2249b87ad4a2c06025fcfab0c1a82f835763cbed993ae2fb
7
+ data.tar.gz: 66ed6d4a45a9ee22feb6470b73771f4e02c1ee38e0de666ff9c8b6e8cb01b242c9be1b20501761100332ba2a50a2a7e4e7144792c671831a4f87446506af50c0
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.1.0
1
+ ruby 3.1.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # 0.5.3
2
+
3
+ - Moved repo to Gitlab
4
+
5
+ # 0.5.2 (June 23, 2022)
6
+
7
+ ## Bug Fixes:
8
+
9
+ - Fix CLI info command, uses list index and now works with Lan controllers
10
+ - Refactored commands
data/Gemfile.lock CHANGED
@@ -1,27 +1,29 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- prologix_gpib (0.4.3)
5
- activesupport
6
- rubyserial
4
+ prologix_gpib (0.5.0)
5
+ activesupport (~> 7.0.1)
6
+ bindata (~> 2.4.10)
7
+ rubyserial (~> 0.6.0)
7
8
  terminal-table (~> 3.0.2)
8
- thor
9
+ thor (~> 1.2.1)
9
10
 
10
11
  GEM
11
12
  remote: https://rubygems.org/
12
13
  specs:
13
- activesupport (7.0.1)
14
+ activesupport (7.0.3)
14
15
  concurrent-ruby (~> 1.0, >= 1.0.2)
15
16
  i18n (>= 1.6, < 2)
16
17
  minitest (>= 5.1)
17
18
  tzinfo (~> 2.0)
19
+ bindata (2.4.10)
18
20
  coderay (1.1.3)
19
- concurrent-ruby (1.1.9)
21
+ concurrent-ruby (1.1.10)
20
22
  ffi (1.15.5)
21
- i18n (1.8.11)
23
+ i18n (1.10.0)
22
24
  concurrent-ruby (~> 1.0)
23
25
  method_source (1.0.0)
24
- minitest (5.15.0)
26
+ minitest (5.16.1)
25
27
  pry (0.14.1)
26
28
  coderay (~> 1.1)
27
29
  method_source (~> 1.0)
@@ -43,7 +45,6 @@ DEPENDENCIES
43
45
  prologix_gpib!
44
46
  pry
45
47
  rake (~> 10.0)
46
- rubyserial (~> 0.6)
47
48
 
48
49
  BUNDLED WITH
49
- 2.2.32
50
+ 2.3.16
data/NetFinder_info.md ADDED
@@ -0,0 +1,30 @@
1
+ # Net Finder
2
+
3
+ NetFinder, uses UDP broadcast to locate and configure GPIB-ETH controllers on a network.
4
+ You can find an implementation of the protocol in the nfcli utility
5
+ http://prologix.biz/downloads/nfcli.tar.gz
6
+
7
+ ### Discovery
8
+
9
+ - Create NF_IDENTIFY packet.
10
+ - Set sequence to a random value
11
+ - Set eth_addr to all ones
12
+ - Broadcast NF_IDENTIFY datagram to port 3040/UDP.
13
+ - Listen for NF_IDENTIFY_REPLY datagrams.
14
+ - Verify sequence matches NF_IDENTIFY packet
15
+
16
+ ### Configuration
17
+
18
+ - Create NF_ASSIGNMENT packet
19
+ - Set sequence to a random value
20
+ - Set eth_addr to MAC address of desired device Set ip_type to NF_IP_DYNAMIC or NF_IP_STATIC If ip_type is NF_IP_DYNAMIC specify ip, mask and gateway addresses.
21
+ - Broadcast NF\* ASSIGNMENT datagram to port 3040/UDP.
22
+ - Listen for NF_ASSIGNMENT_REPLY datagrams.
23
+ - Verify sequence matches that of NF_ASSIGNMENT packet Check result field for NF_SUCCESS
24
+
25
+ Notes:
26
+
27
+ 1. You may get multiple replies. Discard duplicate replies.
28
+ 2. All multi-byte values are in network order (big-endian)
29
+ 3. On multi-homed hosts, make sure broadcasts go out over all interfaces.
30
+ 4. NetFinder protocol only works within the same subnet as routers will not forward UDP broadcast packets.
data/README.md CHANGED
@@ -24,18 +24,20 @@ Or install it yourself as:
24
24
 
25
25
  ### CLI
26
26
 
27
- The gem comes with a simple cli for querying controllers:
27
+ The gem comes with a simple cli for finding controllers:
28
28
 
29
29
  ```bash
30
30
  $ plx list
31
- +-------------------------------------------------------------------------------+
32
- | Prologix Controllers |
33
- +-------+-------------------------------+---------+-----------------------------+
34
- | index | Controller | Version | Path |
35
- +-------+-------------------------------+---------+-----------------------------+
36
- | 0 | Prologix GPIB-USB Controller | 6.101 | /dev/tty.usbserial-PX9HPBMB |
37
- | 1 | Prologix GPIB-USB Controller | 6.107 | /dev/tty.usbserial-PXEGWA9A |
38
- +-------+-------------------------------+---------+-----------------------------+
31
+ +-----------------------------------------------------------------------------------------+
32
+ | Prologix Controllers |
33
+ +-------+------------------------------------+--------------+-----------------------------+
34
+ | index | Controller | Version | Location |
35
+ +-------+------------------------------------+--------------+-----------------------------+
36
+ | 0 | Prologix GPIB-USB Controller | 6.101 | /dev/tty.usbserial-PX9HPBMB |
37
+ | 1 | Prologix GPIB-USB Controller | 6.107 | /dev/tty.usbserial-PXEGWA9A |
38
+ | 2 | Prologix GPIB-LAN Controller | 01.03.00.00 | 192.168.10.161 |
39
+ | 3 | Prologix GPIB-ETHERNET Controller | 01.06.06.00 | 192.168.10.127 |
40
+ +-------+------------------------------------+--------------+-----------------------------+
39
41
 
40
42
  $ plx info 0
41
43
 
@@ -50,24 +52,52 @@ $ plx info 0
50
52
  EOS: Append CR+LF
51
53
  EOT: Enabled
52
54
 
55
+ plx info 3
56
+
57
+ Prologix gpib-ethernet controller
58
+ Path: 192.168.10.127
59
+ Firmware: 01.06.06.00
60
+ Mode: Controller
61
+ Device Address: 5
62
+ Auto Read: Enabled
63
+ Read Timeout: 500
64
+ Eoi Assertion: Enabled
65
+ Eos: Append CR+LF
66
+ Eot: Disabled
53
67
  ```
54
68
 
55
- ### Playing in Console
69
+ ### Finding Controllers
56
70
 
57
- I'm enamoured by this interface, I'd like a more ruby like way of finding and connecting controllers, that works with Ethernet controllers too.
71
+ ```irb
72
+ irb(main):001:0> f = PrologixGpib::Finder.new
73
+ => #<PrologixGpib::Finder:0x00000001089844b0>
74
+ irb(main):002:0> controllers = f.avaliable_controllers
75
+ => {
76
+ :usb=>["/dev/tty.usbserial-PX9HPBMB", "/dev/tty.usbserial-PXEGWA9A"],
77
+ :lan=>["192.168.10.161", "192.168.10.165"]
78
+ }
58
79
 
59
- ```ruby
60
- irb(main):001:0> require 'prologix_gpib'
61
- => true
80
+ ```
81
+
82
+ ### Working with Controllers
62
83
 
63
- irb(main):002:0> paths = PrologixGpib.usb_paths
64
- => ["/dev/tty.usbserial-PX9HPBMB", "/dev/tty.usbserial-PXEGWA9A"]
84
+ I'm not enamoured by this interface, I'd like a more ruby like way of finding and connecting controllers thats less clunky. It works for now, but may change as we refine things.
65
85
 
66
- irb(main):003:0> device = PrologixGpib::UsbController.new(paths[0])
86
+ ```ruby
87
+ irb(main):003:0> device = PrologixGpib::UsbController.new(controllers[:usb][0])
67
88
  => #<PrologixGpib::UsbController:0x00000001574c4098 @serial_port=#<Serial:0x00000001574bfef8 @config=#<RubySerial::Posix::Termios:0x00000001574bf728>, @fd=9, @open=true>>
68
89
 
69
90
  irb(main):004:0> device.config
70
- => {:device_name=>"Prologix GPIB-USB Controller", :firmware=>"6.101", :mode=>"Device", :device_address=>"9", :auto_read=>"NA", :read_timeout=>"NA", :eoi_assertion=>"Enabled", :eos=>"Append CR+LF", :eot=>"Enabled"}
91
+ => {
92
+ :device_name=>"Prologix GPIB-USB Controller",
93
+ :firmware=>"6.101", :mode=>"Device",
94
+ :device_address=>"9",
95
+ :auto_read=>"NA",
96
+ :read_timeout=>"NA",
97
+ :eoi_assertion=>"Enabled",
98
+ :eos=>"Append CR+LF",
99
+ :eot=>"Enabled"
100
+ }
71
101
 
72
102
  irb(main):005:0> device.address
73
103
  => "9"
@@ -79,7 +109,16 @@ irb(main):007:0> device.mode
79
109
  => "0"
80
110
 
81
111
  irb(main):008:0> device.config
82
- => {:device_name=>"Prologix GPIB-USB Controller", :firmware=>"6.101", :mode=>"Controller", :device_address=>"9", :auto_read=>"Disabled", :read_timeout=>"200", :eoi_assertion=>"Enabled", :eos=>"Append CR+LF", :eot=>"Enabled"}
112
+ => {
113
+ :device_name=>"Prologix GPIB-USB Controller",
114
+ :firmware=>"6.101", :mode=>"Controller",
115
+ :device_address=>"9",
116
+ :auto_read=>"Disabled",
117
+ :read_timeout=>"200",
118
+ :eoi_assertion=>"Enabled",
119
+ :eos=>"Append CR+LF",
120
+ :eot=>"Enabled"
121
+ }
83
122
  ```
84
123
 
85
124
  ### Firmware update
data/lan_test.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'prologix_gpib'
2
+ require 'socket'
3
+
4
+ class Scanner
5
+ def search(range, timeout = 0.1)
6
+ array = []
7
+ range.each do |x|
8
+ s = Socket.tcp("192.168.10.#{x}", 1234, connect_timeout: timeout)
9
+ s.write("++ver\r\n")
10
+ array << s.gets
11
+ rescue => error
12
+ puts error.inspect
13
+ next
14
+ end
15
+ array
16
+ end
17
+ end
@@ -2,52 +2,78 @@ require 'thor'
2
2
  require 'pp'
3
3
  require 'prologix_gpib'
4
4
  require 'terminal-table'
5
+ require 'resolv'
5
6
 
6
7
  module PrologixGpib
7
8
  class CLI < Thor
9
+ def initialize(*args)
10
+ super
11
+ @controllers = PrologixGpib::Finder.new.avaliable_controllers
12
+ end
13
+
8
14
  desc 'list', 'List all connected controllers'
9
15
 
10
16
  def list
11
- if controllers_connected?
12
- table =
13
- Terminal::Table.new do |t|
14
- t.title = 'Prologix Controllers'
15
- t.headings = %w[index Controller Version Path]
16
- PrologixGpib.usb_paths.each.with_index do |path, index|
17
- device = PrologixGpib::UsbController.new(path)
18
- str = device.version.split('version')
19
- t.add_row [index.to_s, str[0], str[1], path]
20
- end
21
- end
22
- puts table
23
- else
24
- puts 'No Prologix Controllers available.'
25
- end
17
+ puts controller_table(@controllers)
26
18
  end
27
19
 
28
- desc 'info', 'Display Controller information'
29
- option :path, alias: :p
30
- def info
20
+ desc 'info [INDEX]', 'Display Controller information'
21
+ option :path, aliases: :p
22
+ def info(index)
31
23
  return unless controllers_connected?
32
24
 
33
- paths = options[:path].nil? ? PrologixGpib.usb_paths : [options[:path]]
25
+ controller_paths = @controllers.map { |k, v| v }.flatten
34
26
 
35
- paths.each do |path|
36
- hash = PrologixGpib::UsbController.new(path).config
37
- puts "\n #{titleise hash.delete(:device_name)}"
38
- puts "\tPath: #{path}"
39
- hash.each { |k, v| puts "\t#{titleise(k)}: #{v}" }
40
- end
27
+ path = controller_paths[index.to_i]
28
+ hash = ip_address?(path) ? PrologixGpib::LanController.new(path).config : PrologixGpib::UsbController.new(path).config
29
+
30
+ puts "\n #{titleise hash.delete(:device_name)}"
31
+ puts "\tPath: #{path}"
32
+ hash.each { |k, v| puts "\t#{titleise(k)}: #{v}" }
41
33
  end
42
34
 
43
35
  private
44
36
 
37
+ def controller_table(controllers)
38
+ return 'No Prologix Controllers available.' unless controllers.length > 0
39
+
40
+ table =
41
+ Terminal::Table.new do |t|
42
+ t.title = 'Prologix Controllers'
43
+ t.headings = %w[index Controller Version Location]
44
+ end
45
+
46
+ index = 0
47
+ if controllers.key? :usb
48
+ controllers[:usb].each do |path|
49
+ device = PrologixGpib::UsbController.new(path)
50
+ str = device.version.split('version')
51
+ table.add_row [index.to_s, str[0], str[1], path]
52
+ index += 1
53
+ end
54
+ end
55
+
56
+ if controllers.key? :lan
57
+ controllers[:lan].each do |ip|
58
+ device = PrologixGpib::LanController.new(ip)
59
+ str = device.version.split('version')
60
+ table.add_row [index.to_s, str[0], str[1], ip]
61
+ index += 1
62
+ end
63
+ end
64
+ table
65
+ end
66
+
45
67
  def controllers_connected?
46
- PrologixGpib.usb_paths.count >= 1
68
+ @controllers[:usb].any? || @controllers[:lan].any?
47
69
  end
48
70
 
49
71
  def titleise(string)
50
72
  string.to_s.split('_').map(&:capitalize).join(' ')
51
73
  end
74
+
75
+ def ip_address?(string)
76
+ string =~ Resolv::IPv4::Regex ? true : false
77
+ end
52
78
  end
53
79
  end
@@ -1,4 +1,4 @@
1
- module PrologixGpib::Usb::Commands
1
+ module PrologixGpib::Commands
2
2
  def config
3
3
  error_message = 'Error'
4
4
  device_version = version.split('version').map(&:strip)
@@ -40,7 +40,7 @@ module PrologixGpib::Usb::Commands
40
40
  conf
41
41
  end
42
42
 
43
- # This command configures the Prologix GPIB-USB controller to be a :controller or :device.
43
+ # # This command configures the Prologix GPIB-USB controller to be a :controller or :device.
44
44
  def mode=(op_mode)
45
45
  mode =
46
46
  case op_mode
@@ -60,8 +60,8 @@ module PrologixGpib::Usb::Commands
60
60
  end
61
61
  alias operation_mode mode
62
62
 
63
- # Timeout value, in milliseconds, used in the read command and spoll command.
64
- # Any value between 1 and 3000 milliseconds.
63
+ # # Timeout value, in milliseconds, used in the read command and spoll command.
64
+ # # Any value between 1 and 3000 milliseconds.
65
65
  def timeout=(milliseconds)
66
66
  return unless connected? || milliseconds.class != Integer
67
67
 
@@ -72,8 +72,8 @@ module PrologixGpib::Usb::Commands
72
72
  device_query('++read_tmo_ms')
73
73
  end
74
74
 
75
- # PrologixGPIB-USB controller can be configured to automatically address instruments to 'talk' after sending a command in order to read the response.
76
- # *** Avaliable in Controller mode. When enabled can cause the prologix controller to lockup. ***
75
+ # # PrologixGPIB-USB controller can be configured to automatically address instruments to 'talk' after sending a command in order to read the response.
76
+ # # *** Avaliable in Controller mode. When enabled can cause the prologix controller to lockup. ***
77
77
  def auto=(auto_mode)
78
78
  mode =
79
79
  case auto_mode
@@ -93,8 +93,8 @@ module PrologixGpib::Usb::Commands
93
93
  end
94
94
  alias auto_read_after_write auto
95
95
 
96
- # In :controller mode, address refers to the GPIB address of the instrument being controlled.
97
- # In :device mode, it is the address of the GPIB peripheral that Prologix GPIB-USB controller is emulating.
96
+ # # In :controller mode, address refers to the GPIB address of the instrument being controlled.
97
+ # # In :device mode, it is the address of the GPIB peripheral that Prologix GPIB-USB controller is emulating.
98
98
  def address=(addr)
99
99
  write("++addr #{addr}")
100
100
  end
@@ -104,8 +104,8 @@ module PrologixGpib::Usb::Commands
104
104
  device_query('++addr')
105
105
  end
106
106
 
107
- # This command enables or disables the assertion of the EOI signal with the last character of any command sent over GPIB port.
108
- # Some instruments require EOI signal to be asserted in order to properly detect the end of a command.
107
+ # # This command enables or disables the assertion of the EOI signal with the last character of any command sent over GPIB port.
108
+ # # Some instruments require EOI signal to be asserted in order to properly detect the end of a command.
109
109
  def eoi=(eoi_mode)
110
110
  mode =
111
111
  case eoi_mode
@@ -123,13 +123,13 @@ module PrologixGpib::Usb::Commands
123
123
  device_query('++eoi')
124
124
  end
125
125
 
126
- # This command specifies GPIB termination characters. When data from host is received over USB, all non-escaped LF, CR and ESC characters are removed and GPIB terminators, as specified by this command, are appended before sending the data to instruments.
127
- # This command does not affect data from instruments received over GPIB port.
128
- # EXAMPLES:
129
- # 0 Append CR+LF
130
- # 1 Append CR to instrument commands
131
- # 2 Append LF to instrument commands
132
- # 3 Do not append anything to instrument commands
126
+ # # This command specifies GPIB termination characters. When data from host is received over USB, all non-escaped LF, CR and ESC characters are removed and GPIB terminators, as specified by this command, are appended before sending the data to instruments.
127
+ # # This command does not affect data from instruments received over GPIB port.
128
+ # # EXAMPLES:
129
+ # # 0 Append CR+LF
130
+ # # 1 Append CR to instrument commands
131
+ # # 2 Append LF to instrument commands
132
+ # # 3 Do not append anything to instrument commands
133
133
  def eos=(eos_mode)
134
134
  error_message = "Invalid arg: '#{eos_mode}'"
135
135
  raise ArgumentError, error_message unless [0, 1, 2, 3].include? eos_mode
@@ -141,7 +141,7 @@ module PrologixGpib::Usb::Commands
141
141
  device_query('++eos')
142
142
  end
143
143
 
144
- # This command enables or disables the appending of a user specified character (see eot_char) to USB output whenever EOI is detected while reading a character from the GPIBport.
144
+ # # This command enables or disables the appending of a user specified character (see eot_char) to USB output whenever EOI is detected while reading a character from the GPIBport.
145
145
  def eot=(eot_mode)
146
146
  mode =
147
147
  case eot_mode
@@ -186,18 +186,4 @@ module PrologixGpib::Usb::Commands
186
186
  def reset
187
187
  write('++rst')
188
188
  end
189
-
190
- def flush
191
- return unless connected?
192
-
193
- loop until serial_port.getbyte.nil?
194
- end
195
-
196
- private
197
-
198
- def device_query(command)
199
- flush
200
- write(command)
201
- readline
202
- end
203
189
  end
@@ -0,0 +1,128 @@
1
+ module PrologixGpib::Discovery
2
+ require 'socket'
3
+ require 'ipaddr'
4
+ require 'bindata'
5
+ require 'timeout'
6
+
7
+ class Error < StandardError
8
+ end
9
+
10
+ def avaliable_controllers
11
+ { usb: usb_device_paths, lan: lan_device_ips }
12
+ end
13
+
14
+ class IPAddr < BinData::Primitive
15
+ array :octets, type: :uint8, initial_length: 4
16
+
17
+ def set(val)
18
+ self.octets = val.split(/\./).map(&:to_i)
19
+ end
20
+
21
+ def get
22
+ self.octets.map(&:to_s).join('.')
23
+ end
24
+ end
25
+
26
+ class MacAddr < BinData::Primitive
27
+ array :octets, type: :uint8, initial_length: 6
28
+
29
+ def set(val)
30
+ self.octets = val.split(/:/).map(&:hex)
31
+ end
32
+
33
+ def get
34
+ self.octets.map { |octet| '%02x' % octet }.join(':')
35
+ end
36
+ end
37
+
38
+ class NFHeader < BinData::Record
39
+ endian :big
40
+ uint8 :magic
41
+ uint8 :identify
42
+ uint16 :seq
43
+ mac_addr :eth_addr
44
+ bit16 :reserved, initial_value: 0x0000
45
+ end
46
+
47
+ class NFIdentifyReply < BinData::Record
48
+ endian :big
49
+ nf_header :header
50
+ uint16 :uptime_days
51
+ uint8 :uptime_hrs
52
+ uint8 :uptime_mins
53
+ uint8 :uptime_secs
54
+ uint8 :mode
55
+ uint8 :alert
56
+ uint8 :ip_type
57
+ ip_addr :addr
58
+ ip_addr :netmask
59
+ string :ip_gw, read_length: 4
60
+ string :app_ver, read_length: 4
61
+ string :boot_ver, read_length: 4
62
+ string :hw_ver, read_length: 4
63
+ end
64
+
65
+ NF_MAGIC = 0x5a
66
+ HEADER_FMT = 'CCna8'
67
+ NF_IDENTIFY = 0
68
+ NF_IDENTIFY_REPLY = 1
69
+ BROADCAST_PORT = 3040
70
+ BROADCAST_ADDRESS = '255.255.255.255'
71
+ TIMEOUT = 0.5
72
+
73
+ private
74
+
75
+ def lan_device_ips
76
+ seq = rand(0..65_535)
77
+
78
+ # puts "Seq = #{seq}"
79
+ sock = UDPSocket.new
80
+ sock.setsockopt(:SOL_SOCKET, :SO_BROADCAST, true)
81
+
82
+ # data = [NF_MAGIC, NF_IDENTIFY, seq, "\xFF\xFF\xFF\xFF\xFF\xFF"].pack(HEADER_FMT)
83
+
84
+ data = NFHeader.new
85
+ data.magic = NF_MAGIC
86
+ data.identify = NF_IDENTIFY
87
+ data.seq = seq
88
+ data.eth_addr = 'FF:FF:FF:FF:FF:FF'
89
+
90
+ sock.send(data.to_binary_s, 0, BROADCAST_ADDRESS, BROADCAST_PORT)
91
+ array = []
92
+ replies = []
93
+ begin
94
+ Timeout.timeout(TIMEOUT) do
95
+ while true
96
+ data, addr = sock.recvfrom(1000)
97
+ replies << data
98
+ end
99
+ end
100
+ rescue Timeout::Error
101
+ replies.each do |data|
102
+ begin
103
+ reply = NFIdentifyReply.read(data)
104
+ rescue EOFError
105
+ # About 1% of responses are not always as expected from the controller
106
+ next
107
+ end
108
+ next if array.include?(reply.addr)
109
+ array << reply.addr if reply.header.seq == seq && reply.header.identify == NF_IDENTIFY_REPLY
110
+ end
111
+ sock.close
112
+ array
113
+ end
114
+ end
115
+
116
+ def usb_device_paths
117
+ path_str, dir =
118
+ if RubySerial::ON_LINUX
119
+ %w[ttyUSB /dev/]
120
+ elsif RubySerial::ON_WINDOWS
121
+ ['TODO: Implement find device for Windows', 'You lazy bugger']
122
+ else
123
+ %w[tty.usbserial /dev/]
124
+ end
125
+
126
+ Dir.glob("#{dir}#{path_str}*")
127
+ end
128
+ end
@@ -1,3 +1,70 @@
1
+ require 'timeout'
2
+
1
3
  module PrologixGpib::Lan
2
- class Error < StandardError; end
3
- end
4
+ class Error < StandardError
5
+ end
6
+
7
+ DEVICE_PORT = 1234
8
+ EOL = "\r\n".freeze
9
+
10
+ def initialize(ip, mode: :controller, address: 9)
11
+ open_socket(ip)
12
+
13
+ # open_serial_port(paths)
14
+ # flush
15
+ # self.mode = mode
16
+ # self.address = address
17
+ # self.auto = :disable
18
+ # self.eos = 0
19
+
20
+ yield self if block_given?
21
+ end
22
+
23
+ def write(command)
24
+ return unless connected?
25
+
26
+ @socket.send "#{command}#{EOL}", 0
27
+ sleep 0.1
28
+ end
29
+
30
+ def read
31
+ return unless connected?
32
+
33
+ @socket.gets.chomp
34
+ end
35
+
36
+ private
37
+
38
+ def open_socket(ip)
39
+ @socket = TCPSocket.new ip, DEVICE_PORT
40
+ write('++ver')
41
+ return if getline.include? 'Prologix'
42
+
43
+ raise Error, 'No Prologix LAN controllers found.'
44
+ end
45
+
46
+ def connected?
47
+ raise Error, 'ConnectionError: No open Prologix device connections.' if @socket.nil?
48
+
49
+ true
50
+ end
51
+
52
+ def readline
53
+ return unless connected?
54
+
55
+ t = Timeout.timeout(1, Timeout::Error, "No response from device at #{@socket.peeraddr[3]}") { getline }
56
+ end
57
+
58
+ # This method will block until the EOL terminator is received
59
+ # The lower level gets method is pure ruby, so can be safely used with Timeout.
60
+ def getline
61
+ return unless connected?
62
+
63
+ @socket.gets(EOL).chomp
64
+ end
65
+
66
+ def device_query(command)
67
+ write(command)
68
+ readline
69
+ end
70
+ end
@@ -1,6 +1,5 @@
1
1
  module PrologixGpib::Usb
2
2
  require 'timeout'
3
- require 'prologix_gpib/usb/commands'
4
3
 
5
4
  class Error < StandardError
6
5
  end
@@ -10,8 +9,7 @@ module PrologixGpib::Usb
10
9
  attr_reader :serial_port
11
10
 
12
11
  def initialize(path, mode: :controller, address: 9)
13
- paths = path.nil? ? PrologixGpib.controller_paths : [path]
14
- open_serial_port(paths)
12
+ open_serial_port(path)
15
13
  flush
16
14
  self.mode = mode
17
15
  self.address = address
@@ -29,10 +27,10 @@ module PrologixGpib::Usb
29
27
  @serial_port.nil?
30
28
  end
31
29
 
32
- def write(str)
30
+ def write(string)
33
31
  return unless connected?
34
32
 
35
- @serial_port.write("#{str}#{EOL}")
33
+ @serial_port.write("#{string}#{EOL}")
36
34
  end
37
35
 
38
36
  def read(bytes)
@@ -44,7 +42,7 @@ module PrologixGpib::Usb
44
42
  def readline
45
43
  return unless connected?
46
44
 
47
- t = Timeout.timeout(1, Timeout::Error, 'No response from Data Acquisistion') { getline }
45
+ t = Timeout.timeout(1, Timeout::Error, 'No response from device') { getline }
48
46
  end
49
47
 
50
48
  def sr(register = nil)
@@ -58,12 +56,11 @@ module PrologixGpib::Usb
58
56
 
59
57
  private
60
58
 
61
- def open_serial_port(paths)
62
- paths.each do |path|
63
- @serial_port = Serial.new(path)
64
- write('++ver')
65
- return if getline.include? 'Prologix'
66
- end
59
+ def open_serial_port(path)
60
+ @serial_port = Serial.new(path)
61
+ write('++ver')
62
+ return if getline.include? 'Prologix'
63
+
67
64
  raise Error, 'No Prologix USB controllers found.'
68
65
  end
69
66
 
@@ -73,6 +70,12 @@ module PrologixGpib::Usb
73
70
  true
74
71
  end
75
72
 
73
+ def flush
74
+ return unless connected?
75
+
76
+ loop until serial_port.getbyte.nil?
77
+ end
78
+
76
79
  # This method will block until the EOL terminator is received
77
80
  # The lower level gets method is pure ruby, so can be safely used with Timeout.
78
81
  def getline
@@ -80,4 +83,10 @@ module PrologixGpib::Usb
80
83
 
81
84
  @serial_port.gets(EOL).chomp
82
85
  end
86
+
87
+ def device_query(command)
88
+ flush
89
+ write(command)
90
+ readline
91
+ end
83
92
  end
@@ -1,3 +1,3 @@
1
1
  module PrologixGpib
2
- VERSION = '0.4.4'
2
+ VERSION = '0.5.3'
3
3
  end
data/lib/prologix_gpib.rb CHANGED
@@ -4,46 +4,33 @@ require 'rubyserial'
4
4
  require 'prologix_gpib/version'
5
5
  require 'prologix_gpib/lan'
6
6
  require 'prologix_gpib/usb'
7
+ require 'prologix_gpib/commands'
8
+ require 'prologix_gpib/discovery'
7
9
  require 'prologix_gpib/cli'
8
10
 
9
11
  module PrologixGpib
10
12
  class UsbController
13
+ def test
14
+ puts 'testing'
15
+ end
16
+
11
17
  include PrologixGpib::Usb
12
- include PrologixGpib::Usb::Commands
18
+ include PrologixGpib::Commands
13
19
  end
14
20
 
15
21
  class LanController
16
22
  include PrologixGpib::Lan
23
+ include PrologixGpib::Commands
24
+ end
25
+
26
+ class Finder
27
+ include PrologixGpib::Discovery
17
28
  end
18
29
 
19
- # Ideally this class needs to handle finding all avaliable Prologix GPIB controllers (USB and Ethernet),
20
- # But for now it simply passes the Prologix USB device paths onto the controller class
21
30
  # No windows serial support just yet.
22
31
  class << self
23
32
  def new
24
33
  self
25
34
  end
26
-
27
- # Find first avaliable Prologix controller and return a valid controller object
28
- def open; end
29
-
30
- def usb_paths
31
- usb_controller_paths
32
- end
33
-
34
- private
35
-
36
- def usb_controller_paths
37
- path_str, dir =
38
- if RubySerial::ON_LINUX
39
- %w[ttyUSB /dev/]
40
- elsif RubySerial::ON_WINDOWS
41
- ['TODO: Implement find device for Windows', 'You lazy bugger']
42
- else
43
- %w[tty.usbserial /dev/]
44
- end
45
-
46
- Dir.glob("#{dir}#{path_str}*")
47
- end
48
35
  end
49
36
  end
@@ -12,14 +12,14 @@ Gem::Specification.new do |spec|
12
12
 
13
13
  spec.summary = 'Prologix GPIB controller ruby wrapper.'
14
14
  spec.description = 'Ruby wrapper for the Prologix GPIB controllers, USB & Ethernet.'
15
- spec.homepage = 'https://github.com/robcarruthers/prologix_gpib'
15
+ spec.homepage = 'https://gitlab.com/robcarruthers/prologix_gpib.git'
16
16
  spec.license = 'MIT'
17
17
 
18
18
  # spec.metadata['allowed_push_host'] = "http://mygemserver.com'"
19
19
 
20
20
  spec.metadata['homepage_uri'] = spec.homepage
21
- spec.metadata['source_code_uri'] = 'https://github.com/robcarruthers/prologix_gpib'
22
- spec.metadata['changelog_uri'] = 'https://github.com/robcarruthers/prologix_gpib'
21
+ spec.metadata['source_code_uri'] = 'https://gitlab.com/robcarruthers/prologix_gpib.git'
22
+ spec.metadata['changelog_uri'] = 'https://gitlab.com/robcarruthers/prologix_gpib.git'
23
23
 
24
24
  # Specify which files should be added to the gem when it is released.
25
25
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -29,9 +29,10 @@ Gem::Specification.new do |spec|
29
29
  spec.require_paths = ['lib']
30
30
 
31
31
  spec.add_dependency 'activesupport', '~> 7.0.1'
32
+ spec.add_dependency 'bindata', '~> 2.4.10'
32
33
  spec.add_dependency 'rubyserial', '~> 0.6.0'
33
- spec.add_dependency 'thor', '~> 1.2.1'
34
34
  spec.add_dependency 'terminal-table', '~> 3.0.2'
35
+ spec.add_dependency 'thor', '~> 1.2.1'
35
36
 
36
37
  spec.add_development_dependency 'bundler', '~> 2.0'
37
38
  spec.add_development_dependency 'rake', '~> 10.0'
data/test_broadcast.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'socket'
2
+
3
+ def broadcast
4
+ addr = '255.255.255.255'
5
+ sock = UDPSocket.new
6
+ sock.setsockopt(:SOL_SOCKET, :SO_BROADCAST, true)
7
+
8
+ port = 3040
9
+
10
+ data = [90, 0, 11_111, "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00"].pack('CCna8')
11
+ sock.send(data, 0, addr, port)
12
+
13
+ while true
14
+ r_data, r_addr = sock.recvfrom(2000) # if this number is too low it will drop the larger packets and never give them to you
15
+ p "From addr: #{r_addr}, msg: #{r_data}"
16
+ # p r_data.unpack('CCna8')
17
+ # puts ''
18
+ end
19
+
20
+ sock.close
21
+ end
22
+
23
+ # ❯ python
24
+
25
+ # WARNING: Python 2.7 is not recommended.
26
+ # This version is included in macOS for compatibility with legacy software.
27
+ # Future versions of macOS will not include Python 2.7.
28
+ # Instead, it is recommended that you transition to using 'python3' from within Terminal.
29
+
30
+ # Python 2.7.18 (default, Nov 13 2021, 06:17:34)
31
+ # [GCC Apple LLVM 13.0.0 (clang-1300.0.29.10) [+internal-os, ptrauth-isa=deployme on darwin
32
+ # Type "help", "copyright", "credits" or "license" for more information.
33
+ # >>> "Z\x00ST\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00".unpack("!2cH6s2x")
34
+ # Traceback (most recent call last):
35
+ # File "<stdin>", line 1, in <module>
36
+ # AttributeError: 'str' object has no attribute 'unpack'
37
+ # >>> struct.unpack("!2cH6s2x", "Z\x00ST\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00")
38
+ # Traceback (most recent call last):
39
+ # File "<stdin>", line 1, in <module>
40
+ # NameError: name 'struct' is not defined
41
+ # >>> import struct
42
+ # >>> struct.unpack("!2cH6s2x", "Z\x00ST\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00")
43
+ # ('Z', '\x00', 21332, '\xff\xff\xff\xff\xff\xff')
44
+ # >>> struct.unpack("!2cH6s2x", "Z\x00s|\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00")
45
+ # ('Z', '\x00', 29564, '\xff\xff\xff\xff\xff\xff')
46
+ # >>> struct.unpack("!2cH6s2x", 'Z\x00+g\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00')
47
+ # ('Z', '\x00', 11111, '\xff\xff\xff\xff\xff\xff')
48
+ # >>>
49
+ # 5a012b670021690100d700000000020324010000c0a80aa1ffffff00c0a80a01010300000101000001010000a5bba57bf645122ba7773d197aa7963448eb8c932a85527a24b3966096caa2ef
50
+ # "\x5a\x01\x2b\x67\x00\x21\x69\x01\x00\xd7\x00\x00\x00\x00\x02\x03\x24\x01\x00\x00\xc0\xa8\x0a\xa1\xff\xff\xff\x00\xc0\xa8\x0a\x01\x01\x03\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\xa5\xbb\xa5\x7b\xf6\x45\x12\x2b\xa7\x77\x3d\x19\x7a\xa7\x96\x34\x48\xeb\x8c\x93\x2a\x85\x52\x7a\x24\xb3\x96\x60\x96\xca\xa2\xef"
data/test_script.rb CHANGED
@@ -1,6 +1,45 @@
1
1
  #!/usr/bin/env ruby
2
- require 'prologix_gpib'
3
- paths = PrologixGpib.usb_paths
2
+ require 'bindata'
4
3
 
5
- device = PrologixGpib::UsbController.new(paths[0])
6
- puts device.config
4
+ class NFHeader < BinData::Record
5
+ endian :big
6
+ uint8 :magic
7
+ uint8 :identify
8
+ uint16 :seq
9
+ string :addr, read_length: 6
10
+ string :res, read_length: 2
11
+ end
12
+
13
+ class IPAddr < BinData::Primitive
14
+ array :octets, type: :uint8, initial_length: 4
15
+
16
+ def set(val)
17
+ self.octets = val.split(/\./).map(&:to_i)
18
+ end
19
+
20
+ def get
21
+ self.octets.map(&:to_s).join('.')
22
+ end
23
+ end
24
+
25
+ class NFIdentifyReply < BinData::Record
26
+ endian :big
27
+ nf_header :header
28
+ uint16 :uptime_days
29
+ uint8 :uptime_hrs
30
+ uint8 :uptime_mins
31
+ uint8 :uptime_secs
32
+ uint8 :mode
33
+ uint8 :alert
34
+ uint8 :ip_type
35
+ ip_addr :addr
36
+ ip_addr :netmask
37
+ string :ip_gw, read_length: 4
38
+ string :app_ver, read_length: 4
39
+ string :boot_ver, read_length: 4
40
+ string :hw_ver, read_length: 4
41
+ end
42
+
43
+ @identify = "Z\x00+g\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00"
44
+ @indentify_reply =
45
+ 'Z\x01+g\x00!i\x01\x00\xd7\x00\x00\x00\x00\x02\x03$\x01\x00\x00\xc0\xa8\n\xa1\xff\xff\xff\x00\xc0\xa8\n\x01\x01\x03\x00\x00\x01\x01\x00\x00\x01\x01\x00\x00\xa5\xbb\xa5{\xf6E\x12+\xa7w=\x19z\xa7\x964H\xeb\x8c\x93*\x85Rz$\xb3\x96`\x96\xca\xa2\xef'
data/test_server.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'socket'
2
+ require 'ipaddr'
3
+
4
+ class UDPTest
5
+ def listen
6
+ s = UDPSocket.new
7
+
8
+ # membership = IPAddr.new(multicast_addr).hton + IPAddr.new(bind_addr).hton
9
+ # s.setsockopt(:IPPROTO_UDP, :IP_ADD_MEMBERSHIP, membership)
10
+
11
+ s.bind('', 3040)
12
+ while true
13
+ data, addr = s.recvfrom(1024)
14
+ puts "addr = #{addr}\r\ndata ="
15
+ p data
16
+ end
17
+ end
18
+
19
+ def start_server
20
+ multi_addr = '225.1.1.1'
21
+ bind_addr = '0.0.0.0'
22
+
23
+ sock = UDPSocket.new
24
+ membership = IPAddr.new(multi_addr).hton + IPAddr.new(bind_addr).hton
25
+ sock.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, membership)
26
+ sock.bind(bind_addr, 3040)
27
+ while true
28
+ data, addr = sock.recvfrom(2000) # if this number is too low it will drop the larger packets and never give them to you
29
+ p "From addr: #{addr}, msg: #{data}"
30
+ p data.unpack('CCna8')
31
+ puts ''
32
+ end
33
+ sock.close
34
+ end
35
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prologix_gpib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Carruthers
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-12 00:00:00.000000000 Z
11
+ date: 2022-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -25,33 +25,33 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: 7.0.1
27
27
  - !ruby/object:Gem::Dependency
28
- name: rubyserial
28
+ name: bindata
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.6.0
33
+ version: 2.4.10
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.6.0
40
+ version: 2.4.10
41
41
  - !ruby/object:Gem::Dependency
42
- name: thor
42
+ name: rubyserial
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.2.1
47
+ version: 0.6.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.2.1
54
+ version: 0.6.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: terminal-table
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 3.0.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: thor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.2.1
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.2.1
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -105,8 +119,10 @@ files:
105
119
  - ".DS_Store"
106
120
  - ".gitignore"
107
121
  - ".tool-versions"
122
+ - CHANGELOG.md
108
123
  - Gemfile
109
124
  - Gemfile.lock
125
+ - NetFinder_info.md
110
126
  - PrologixGpibEthernetManual.pdf
111
127
  - PrologixGpibUsbManual-6.0.pdf
112
128
  - README.md
@@ -114,23 +130,27 @@ files:
114
130
  - bin/console
115
131
  - bin/setup
116
132
  - exe/plx
133
+ - lan_test.rb
117
134
  - lib/prologix_gpib.rb
118
135
  - lib/prologix_gpib/cli.rb
136
+ - lib/prologix_gpib/commands.rb
137
+ - lib/prologix_gpib/discovery.rb
119
138
  - lib/prologix_gpib/lan.rb
120
139
  - lib/prologix_gpib/usb.rb
121
- - lib/prologix_gpib/usb/commands.rb
122
140
  - lib/prologix_gpib/version.rb
123
141
  - package.json
124
142
  - prologix_gpib.gemspec
143
+ - test_broadcast.rb
125
144
  - test_script.rb
145
+ - test_server.rb
126
146
  - yarn.lock
127
- homepage: https://github.com/robcarruthers/prologix_gpib
147
+ homepage: https://gitlab.com/robcarruthers/prologix_gpib.git
128
148
  licenses:
129
149
  - MIT
130
150
  metadata:
131
- homepage_uri: https://github.com/robcarruthers/prologix_gpib
132
- source_code_uri: https://github.com/robcarruthers/prologix_gpib
133
- changelog_uri: https://github.com/robcarruthers/prologix_gpib
151
+ homepage_uri: https://gitlab.com/robcarruthers/prologix_gpib.git
152
+ source_code_uri: https://gitlab.com/robcarruthers/prologix_gpib.git
153
+ changelog_uri: https://gitlab.com/robcarruthers/prologix_gpib.git
134
154
  post_install_message:
135
155
  rdoc_options: []
136
156
  require_paths:
@@ -146,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
166
  - !ruby/object:Gem::Version
147
167
  version: '0'
148
168
  requirements: []
149
- rubygems_version: 3.3.3
169
+ rubygems_version: 3.3.7
150
170
  signing_key:
151
171
  specification_version: 4
152
172
  summary: Prologix GPIB controller ruby wrapper.