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 +4 -4
- data/README.md +7 -9
- data/config/matrix_creator.yml +26 -0
- data/lib/matrix_creator.rb +25 -2
- data/lib/matrix_creator/comm.rb +265 -0
- data/lib/matrix_creator/driver_base.rb +47 -0
- data/lib/matrix_creator/everloop.rb +48 -0
- data/lib/matrix_creator/everloop/animation.rb +67 -0
- data/lib/matrix_creator/everloop/color.rb +63 -0
- data/lib/matrix_creator/everloop/pulse.rb +62 -0
- data/lib/matrix_creator/everloop/spinner.rb +52 -0
- data/lib/matrix_creator/humidity.rb +48 -0
- data/lib/matrix_creator/imu.rb +48 -0
- data/lib/matrix_creator/pressure.rb +48 -0
- data/lib/matrix_creator/uv.rb +48 -0
- data/lib/matrix_creator/version.rb +3 -1
- data/lib/matrix_creator/vision.rb +174 -0
- data/lib/protos/malos/driver_pb.rb +339 -0
- data/lib/protos/vision/vision_pb.rb +132 -0
- data/lib/protos/vision/vision_service_pb.rb +19 -0
- metadata +96 -17
- data/.gitignore +0 -12
- data/.rspec +0 -2
- data/.travis.yml +0 -5
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -6
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/matrix_creator.gemspec +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d3dd6fc5e8a17ab1866611eadf427252ddfe0d46
|
4
|
+
data.tar.gz: 0ba05783b39c14f454a93a591905c23cdbf704f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f801925095f6649e709e0eb3a5cceecc203e53d8e2c17ca2748aef46628632e1f63e840b63c481969727056f603c11ced15d12704a791dbcd8d43684586c095
|
7
|
+
data.tar.gz: fc289525820b176c474713611771a53bfa5f67331524263f845cbf472d1ab323deb5eb576bc116f30e3a23fd544ed32c634c23fa6b6f5d9269d8853ad52f0ff6
|
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# MatrixCreator
|
2
2
|
|
3
|
-
|
3
|
+
Abstraction level RubyGem for the [MATRIX Creator](https://creator.matrix.one/) device to interact with its sensors and interfaces.
|
4
4
|
|
5
|
-
|
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
|
-
|
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/
|
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 [
|
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
|
data/lib/matrix_creator.rb
CHANGED
@@ -1,5 +1,28 @@
|
|
1
|
-
require
|
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
|
-
#
|
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
|