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 +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
|