matrix_creator 0.0.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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