matrix_creator 0.0.0 → 1.0.0

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
  SHA1:
3
- metadata.gz: d21b212be4bd23cc70b6acf2c1e1a4494c991a48
4
- data.tar.gz: e9aa9dd8aa1913ad4edc004bb9b2fcbc7291d30d
3
+ metadata.gz: d3dd6fc5e8a17ab1866611eadf427252ddfe0d46
4
+ data.tar.gz: 0ba05783b39c14f454a93a591905c23cdbf704f7
5
5
  SHA512:
6
- metadata.gz: 85cbc478d93e0662318120b07116c24aa616ecfbc3e1aa5a30b5380b80a13fc4ace651b47479fe7d3eda8de286853dce4815abbe77b15d0daa81c4bf9199cf93
7
- data.tar.gz: 80e623c607debe434c081eaed03383cd9b0ad275b61a41a4510a1d70581f59adbdcabe991de823e9584ca2ac0dcab4ed2150f50e6915e1532a037efa2c7f08bc
6
+ metadata.gz: 1f801925095f6649e709e0eb3a5cceecc203e53d8e2c17ca2748aef46628632e1f63e840b63c481969727056f603c11ced15d12704a791dbcd8d43684586c095
7
+ data.tar.gz: fc289525820b176c474713611771a53bfa5f67331524263f845cbf472d1ab323deb5eb576bc116f30e3a23fd544ed32c634c23fa6b6f5d9269d8853ad52f0ff6
data/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # MatrixCreator
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/matrix_creator`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Abstraction level RubyGem for the [MATRIX Creator](https://creator.matrix.one/) device to interact with its sensors and interfaces.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ ## Dependencies
6
+
7
+ TODO: Include dependencies needed for a brand news RaspberryPi installation
6
8
 
7
9
  ## Installation
8
10
 
@@ -22,20 +24,16 @@ Or install it yourself as:
22
24
 
23
25
  ## Usage
24
26
 
25
- TODO: Write usage instructions here
27
+ For usage instructions you can refer to our [Wiki](https://github.com/vikonava/matrix_creator/wiki).
26
28
 
27
29
  ## Development
28
30
 
29
31
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
32
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
-
33
33
  ## Contributing
34
34
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/matrix_creator. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36
-
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/vikonava/matrix_creator. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
37
36
 
38
37
  ## License
39
38
 
40
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
-
39
+ The gem is available as open source under the terms of the [GNU AGPLv3](http://www.gnu.org/licenses/agpl-3.0.txt).
@@ -0,0 +1,26 @@
1
+ matrix:
2
+ ip: '127.0.0.1'
3
+ port_offset:
4
+ base: 0
5
+ error: 1
6
+ keep_alive: 2
7
+ data: 3
8
+ devices:
9
+ imu:
10
+ port: 20013
11
+ humidity:
12
+ port: 20017
13
+ everloop:
14
+ port: 20021
15
+ pressure:
16
+ port: 20025
17
+ uv:
18
+ port: 20029
19
+ zigbee_bulb:
20
+ port: 20033
21
+ microphone:
22
+ port: 20037
23
+ lirc:
24
+ port: 20041
25
+ vision:
26
+ port: 22013
@@ -1,5 +1,28 @@
1
- require "matrix_creator/version"
1
+ require 'google/protobuf'
2
+ require 'yaml'
3
+ require 'json'
4
+ require 'timeout'
5
+ require 'rbczmq'
2
6
 
7
+ require 'matrix_creator/version'
8
+
9
+ ##
10
+ # Main module for Matrix Creator
3
11
  module MatrixCreator
4
- # Your code goes here...
12
+ # Returns a hash of settings to be used by Matrix Creator
13
+ #
14
+ # @return [Hash] the symbolized names object from config/matrix_creator.yml
15
+ def self.settings
16
+ @@_matrix_creator_config ||= JSON.parse(
17
+ JSON.dump(YAML.load_file('config/matrix_creator.yml')),
18
+ symbolize_names: true
19
+ )[:matrix]
20
+ end
5
21
  end
22
+
23
+ require 'matrix_creator/everloop'
24
+ require 'matrix_creator/vision'
25
+ require 'matrix_creator/imu'
26
+ require 'matrix_creator/humidity'
27
+ require 'matrix_creator/pressure'
28
+ require 'matrix_creator/uv'
@@ -0,0 +1,265 @@
1
+ require 'fileutils'
2
+ require 'json'
3
+
4
+ module MatrixCreator
5
+ ##
6
+ # Class: Comm
7
+ #
8
+ # This class is used to communicate to the chipset by using ZeroMQ sockets
9
+ class Comm # rubocop:disable Metrics/ClassLength
10
+ # Contains the IP address to be used to connect to the Matrix Creator chipset
11
+ MATRIX_CREATOR_IP = MatrixCreator.settings[:ip]
12
+
13
+ # Maximum number of old log files to keep
14
+ MAX_OLD_FILES = 10
15
+
16
+ # Maximum size for each log file
17
+ MAX_LOG_SIZE = 102_400_000
18
+
19
+ # Current logger level to store
20
+ LOG_LEVEL = Logger::WARN
21
+
22
+ # Pinging speed
23
+ PING_SPEED = 3
24
+
25
+ # Speed to check for timeout
26
+ TIMEOUT_VERIFICATION_SPEED = 1
27
+
28
+ # Contains the ZMQ::Context instance used
29
+ attr_reader :context
30
+
31
+ # Contains device base port
32
+ attr_reader :device_port
33
+
34
+ ##
35
+ # Creates an instance of Comm to be used as communication with chipset's device
36
+ #
37
+ # @param device_port [Integer] port of the device to communicate with
38
+ def initialize(device_port)
39
+ initialize_logger
40
+
41
+ # Creating instance variables
42
+ print_log(:debug, 'Initializing instance')
43
+ @context = ::ZMQ::Context.new
44
+ @device_port = device_port
45
+ end
46
+
47
+ ##
48
+ # Sends configuration data to the driver
49
+ #
50
+ # @param driver_config [MatrixMalos::DriverConfig] data message
51
+ def send_configuration(driver_config)
52
+ # Connecting to the configuration port
53
+ socket_address = "tcp://#{MATRIX_CREATOR_IP}:#{@device_port}"
54
+ config_socket = @context.socket(:PUSH)
55
+ config_socket.connect(socket_address)
56
+ print_log(:debug, "config_socket connected to #{socket_address}")
57
+
58
+ # Sending Encoded Data
59
+ config_data = MatrixMalos::DriverConfig.encode(driver_config)
60
+ config_socket.send(config_data)
61
+ print_log(:info, 'Configuration sent to driver')
62
+ print_log(:debug, "Data: #{driver_config.to_json}")
63
+ end
64
+
65
+ ##
66
+ # Pings the driver keep-alive port every 3 seconds until listener finishes running
67
+ #
68
+ # @param main_thread [Thread] the main listener
69
+ # @return [Thread] instance
70
+ def start_pinging(main_thread)
71
+ Thread.new do
72
+ # Connecting to the keep-alive port
73
+ socket_address = "tcp://#{MATRIX_CREATOR_IP}:#{@device_port + 1}"
74
+ ping_socket = @context.socket(:PUSH)
75
+ ping_socket.connect(socket_address)
76
+ print_log(:debug, "ping_socket connected to #{socket_address}")
77
+
78
+ # Infinite loop that breaks when main thread has finished
79
+ loop do
80
+ # Send Ping
81
+ ping_socket.send('')
82
+ print_log(:info, 'Ping sent')
83
+
84
+ sleep(PING_SPEED)
85
+
86
+ break if main_thread[:finished]
87
+ end
88
+
89
+ print_log(:debug, 'Stopped pinging')
90
+ end
91
+ end
92
+
93
+ ##
94
+ # Connects to the error port to listen for any errors reported
95
+ #
96
+ # @return [Thread] instance
97
+ def start_error_listener
98
+ Thread.new do
99
+ # Connecting to the error port
100
+ socket_address = "tcp://#{MATRIX_CREATOR_IP}:#{@device_port + 2}"
101
+ error_socket = @context.socket(:SUB)
102
+ error_socket.connect(socket_address)
103
+ error_socket.subscribe('')
104
+
105
+ # Infinite loop to listen for errors, this thread will be killed
106
+ # by the main thread when it needs to be stopped
107
+ loop do
108
+ # Read and log error messages
109
+ error_msg = error_socket.recv_message
110
+ print_log(:error, error_msg.data)
111
+ end
112
+ end
113
+ end
114
+
115
+ ##
116
+ # Main thread that listens for data reported by the driver data port.
117
+ # It will listen for any errors until the maximum number of messages expected
118
+ # to be received is reached or until it is killed by the timeout verification.
119
+ #
120
+ # @param decoder [MatrixMalos] module to be used to decode data received
121
+ # @param max_resp [Integer] maximum number of messages to receive
122
+ # @param error_thread [Thread] instance that logs errors
123
+ # @param block callback method to be executed when a message has been received
124
+ # @return [Thread] instance
125
+ def start_data_listener(decoder, max_resp, error_thread, block = nil)
126
+ Thread.new do
127
+ # Initialize current number of messages received
128
+ count = 0
129
+
130
+ begin
131
+ # Thread variable that indicates if this thread has finished
132
+ Thread.current[:finished] = false
133
+
134
+ # Thread variable that contains an array of messages received
135
+ # for further processing
136
+ Thread.current[:result] = []
137
+
138
+ # Connecting to the data port
139
+ socket_address = "tcp://#{MATRIX_CREATOR_IP}:#{@device_port + 3}"
140
+ data_socket = @context.socket(:SUB)
141
+ data_socket.connect(socket_address)
142
+ print_log(:debug, "data_socket connected to #{socket_address}")
143
+ data_socket.subscribe('')
144
+ print_log(:info, "Listening for data (max_resp: #{max_resp || 'Unlimited'})")
145
+
146
+ loop do
147
+ # Receiving data
148
+ data = data_socket.recv
149
+ print_log(:info, 'Data received')
150
+ decoded_data = JSON.parse(decoder.decode(data).to_json, symbolize_names: true)
151
+ print_log(:debug, "Data: #{decoded_data}")
152
+
153
+ # Push decoded data into the results array
154
+ Thread.current[:result] << decoded_data
155
+
156
+ # Send data to callback method
157
+ block.call(decoded_data) if block
158
+
159
+ # Increment count and break loop if max number of
160
+ # messages has been reached
161
+ count += 1
162
+ break if max_resp && count >= max_resp
163
+ end
164
+ rescue => e
165
+ print_log(:fatal, e.message)
166
+ end
167
+
168
+ # Mark thread as finished
169
+ Thread.current[:finished] = true
170
+ print_log(:info, 'Finished listening')
171
+
172
+ # Kill error thread, no longer need to log errors
173
+ Thread.kill(error_thread)
174
+ print_log(:info, 'Killed error listener thread')
175
+ end
176
+ end
177
+
178
+ ##
179
+ # Verifies if there is a timeout according to the max number of seconds specified,
180
+ # if there is then all threads are killed
181
+ #
182
+ # @param max_secs [Integer] maximum number of seconds to gather data
183
+ # @param main_thread [Thread] instance of the main data listener
184
+ # @param error_thread [Thread] instance of the error listener
185
+ # @param ping_thread [Thread] instance of the ping thread
186
+ def verify_timeout(max_secs, main_thread, error_thread, ping_thread)
187
+ current_time = Time.now
188
+
189
+ print_log(:info, "Starting timeout verification (max_secs: #{max_secs})")
190
+
191
+ loop do
192
+ # Break if main thread is finished, we no longer need to check for timeout
193
+ break if main_thread[:finished]
194
+
195
+ # If there is a timeout, kill all threads and break
196
+ if Time.now >= current_time + max_secs
197
+ print_log(:info, 'Listener timed out, killing all threads')
198
+ Thread.kill(main_thread)
199
+ Thread.kill(error_thread)
200
+ Thread.kill(ping_thread)
201
+ break
202
+ end
203
+
204
+ sleep(TIMEOUT_VERIFICATION_SPEED)
205
+ end
206
+
207
+ print_log(:info, 'Finishing timeout verification')
208
+ end
209
+
210
+ ##
211
+ # Start the listening proccess on a driver.
212
+ #
213
+ # @param decoder [MatrixMalos] module to be used to decode data received
214
+ # @param options [Hash] contains the options that can be specified for a max_resp and/or max_secs
215
+ # @yield callback used to process data received from the driver
216
+ # @return an array with a list of all the messages received
217
+ def perform(decoder, options = {}, block = nil)
218
+ # Start running threads
219
+ error_thread = start_error_listener
220
+ data_thread = start_data_listener(decoder, options[:max_resp], error_thread, block)
221
+ ping_thread = start_pinging(data_thread)
222
+
223
+ # Verify timeout if that option is specified
224
+ if options[:max_secs]
225
+ verify_timeout(options[:max_secs], data_thread, error_thread, ping_thread)
226
+ end
227
+
228
+ # Wait for threads to finish
229
+ data_thread.join
230
+ error_thread.join
231
+ ping_thread.join
232
+
233
+ # Return data captured from the driver
234
+ print_log(:debug, "Data Result: #{data_thread[:result].to_json}")
235
+ data_thread[:result]
236
+ end
237
+
238
+ ##
239
+ # Destroy the ZMQ::Context instance, since there can only be one running per proccess
240
+ def destroy
241
+ print_log(:info, 'Destroying ZMQ context')
242
+ @context.destroy
243
+ end
244
+
245
+ private
246
+
247
+ ##
248
+ # Initialize logger instance
249
+ def initialize_logger
250
+ # Logger initialization
251
+ FileUtils.mkdir_p('log/') unless File.directory?('log/')
252
+ @logger = Logger.new('log/matrix_creator.log', MAX_OLD_FILES, MAX_LOG_SIZE)
253
+ @logger.level = LOG_LEVEL
254
+ end
255
+
256
+ ##
257
+ # Send a message to the logger instance
258
+ #
259
+ # @param level [Symbol] logging level for the message
260
+ # @param msg [String] message to be logged
261
+ def print_log(level, msg)
262
+ @logger.send(level, "[Instance: #{object_id}] #{msg}")
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,47 @@
1
+ # Load Protos
2
+ require 'protos/malos/driver_pb'
3
+
4
+ # Load Dependencies
5
+ require 'matrix_creator/comm'
6
+
7
+ module MatrixCreator
8
+ # Module: DriverBase
9
+ #
10
+ # Base communication for generic drivers
11
+ module DriverBase
12
+ ##
13
+ # Detects and returns information from a generic driver
14
+ #
15
+ # @param base_port [Integer] indicates the base port to communicate to the driver
16
+ # @param decoder ProtoBuf to use for decoding returned data
17
+ # @param options [Hash] of keys and values that can contain speed, max_resp and/or max_secs
18
+ # @return [Array] elements detected in JSON format
19
+ #
20
+ # @example Detect value from a driver and return data
21
+ # MatrixCreator::DriverBase.detect(8888, MatrixMalos::Imu, max_resp: 3)
22
+ #
23
+ # @example Detect value from a driver and process data immediatly when received
24
+ # MatrixCreator::DriverBase.detect(8888, MatrixMalos::Imu, max_resp: 3) { |data|
25
+ # // Do something with data
26
+ # }
27
+ #
28
+ def self.detect(base_port, decoder, options = {}, block = nil)
29
+ @driver_comm = MatrixCreator::Comm.new(base_port)
30
+
31
+ # Setup Driver
32
+ config = MatrixMalos::DriverConfig.new(
33
+ delay_between_updates: options[:speed] || 1.0,
34
+ timeout_after_last_ping: 4.0
35
+ )
36
+ @driver_comm.send_configuration(config)
37
+
38
+ # Query Driver
39
+ result = @driver_comm.perform(decoder, options, block)
40
+
41
+ # Destroy context
42
+ @driver_comm.destroy
43
+
44
+ result
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,48 @@
1
+ # Load Protos
2
+ require 'protos/malos/driver_pb'
3
+
4
+ # Load Dependencies
5
+ require 'matrix_creator/comm'
6
+ require 'matrix_creator/everloop/color'
7
+ require 'matrix_creator/everloop/spinner'
8
+ require 'matrix_creator/everloop/pulse'
9
+
10
+ module MatrixCreator
11
+ # Module: Everloop
12
+ #
13
+ # Communicate with the Everloop driver
14
+ module Everloop
15
+ # Configuration values for the Everloop driver
16
+ EVERLOOP_CONFIG = MatrixCreator.settings[:devices][:everloop]
17
+
18
+ # Base port to send data to Everloop driver
19
+ BASE_PORT = EVERLOOP_CONFIG[:port]
20
+
21
+ ##
22
+ # Change the color of all of the Leds on the Everloop driver
23
+ #
24
+ # @param color [Hash] with the rgb+w values for the color
25
+ #
26
+ # @example Change leds using predetermined color
27
+ # MatrixCreator::Everloop.modify_color(MatrixCreator::Everloop::Color::GREEN)
28
+ #
29
+ # @example Change leds using custom
30
+ # MatrixCreator::Everloop.modify_color({ r: 5, g: 3, b: 9, w: 0 })
31
+ #
32
+ def self.modify_color(color)
33
+ everloop_comm = MatrixCreator::Comm.new(BASE_PORT)
34
+
35
+ # Generate 35 instances of LedValue with the same color
36
+ image = (1..35).map do
37
+ MatrixMalos::LedValue.new(red: color[:r], green: color[:g],
38
+ blue: color[:b], white: color[:w])
39
+ end
40
+
41
+ everloop_image = MatrixMalos::EverloopImage.new(led: image)
42
+ msg = MatrixMalos::DriverConfig.new(image: everloop_image)
43
+ everloop_comm.send_configuration(msg)
44
+
45
+ everloop_comm.destroy
46
+ end
47
+ end
48
+ end