apiotics_factory 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/License +9 -0
- data/bin/apiotics_factory +3 -0
- data/lib/apiotics_factory/clear_credentials.rb +25 -0
- data/lib/apiotics_factory/configuration.rb +30 -0
- data/lib/apiotics_factory/generator.rb +133 -0
- data/lib/apiotics_factory/my_command.rb +15 -0
- data/lib/apiotics_factory/portal.rb +33 -0
- data/lib/apiotics_factory/publish.rb +83 -0
- data/lib/apiotics_factory/underscore.rb +11 -0
- data/lib/apiotics_factory/version.rb +3 -0
- data/lib/apiotics_factory.rb +28 -0
- data/lib/templates/apiotics_driver.rb.erb +244 -0
- data/lib/templates/apiotics_interfaces.rb.erb +33 -0
- data/lib/templates/config.json.erb +172 -0
- data/lib/templates/exec.rb.erb +8 -0
- data/lib/templates/grove_pi/grove_pi.rb +270 -0
- data/lib/templates/grove_pi/i2c/device/acm1602ni.rb +19 -0
- data/lib/templates/grove_pi/i2c/device/adt7410.rb +149 -0
- data/lib/templates/grove_pi/i2c/device/aqm0802.rb +70 -0
- data/lib/templates/grove_pi/i2c/device/bmp180.rb +207 -0
- data/lib/templates/grove_pi/i2c/device/d6t-44l.rb +52 -0
- data/lib/templates/grove_pi/i2c/device/hd44780.rb +172 -0
- data/lib/templates/grove_pi/i2c/device/hdc1000.rb +42 -0
- data/lib/templates/grove_pi/i2c/device/mpl115a2.rb +38 -0
- data/lib/templates/grove_pi/i2c/driver/gpio.rb +213 -0
- data/lib/templates/grove_pi/i2c/driver/i2c-dev.rb +60 -0
- data/lib/templates/grove_pi/i2c/driver.rb +28 -0
- data/lib/templates/grove_pi/i2c/i2c.rb +45 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 57b5011c4903e6ef99d831b9ae85710287b6acc5
|
4
|
+
data.tar.gz: 25d2c749da32bdf104eada7bf53eb272fcaa8eb4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 12b1eb3b83b55b53141a60a7b43515c57ef152530c532f67fdcacc6be0fff2bbfc6e8114dc929c53869004fa20e025f86a0c0fcc554f903f0796b8c4354a1fac
|
7
|
+
data.tar.gz: 5efec3d5df04768067213d6a421f7783846f3da221d5739e01aebc36f70f9fd9e3555df87e2de5721f2ba7925db0319e76cce2d65e082fb2d2be818d0fa33e7f
|
data/License
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Copyright 2018 MicroArx Corporation
|
2
|
+
|
3
|
+
Permission is hereby granted to use the Apiotics_Factory Gem (the "Gem") to create driver software. Software created using the Gem or with its assistance may only be distributed using Apiotics websites such as portal.apiotics.com, and may only be installed on third party devices (devices other than those belonging to the developer of the driver software or to MicroArx Corporation) using Apiotics development tools such as the Apiotics Gem. Software created using this Gem or with the assistance of this Gem may only be installed on third party devices which are running operating system images or firmware provided by Apiotics or distributed via the Apiotics websites and installed with Apiotics development tools.
|
4
|
+
|
5
|
+
Permission is not granted to install or use this software to subvert the Apiotics website, development tools, operating system images or firmware with intent to install driver software on operating systems or firmware other than those provided by Apiotics and installed with Apiotics development tools, to use Apiotics software or development tools in any way without paying the subscription plan fees as published on portal.apiotics.com, or with any other intent or purpose.
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'thor/group'
|
2
|
+
|
3
|
+
module ApioticsFactory
|
4
|
+
class ClearCredentials < Thor::Group
|
5
|
+
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
desc "Deletes vendor and portal credentials."
|
9
|
+
|
10
|
+
def self.source_root
|
11
|
+
Dir.pwd
|
12
|
+
end
|
13
|
+
|
14
|
+
def delete_credentials
|
15
|
+
config_dir = File.dirname(__FILE__).chomp("/lib/apiotics_factory") + "/var"
|
16
|
+
#puts config_dir
|
17
|
+
if File.exist?(config_dir + "/config.json")
|
18
|
+
puts "Deleting config file #{config_dir}" + "/config.json"
|
19
|
+
File.delete(config_dir + "/config.json")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ApioticsFactory
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
attr_accessor :public_key, :private_key, :portal, :interface_kinds
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@public_key = nil
|
8
|
+
@private_key = nil
|
9
|
+
@portal = "https://portal.apiotics.com/"
|
10
|
+
@interface_kinds = {
|
11
|
+
"string" => "string",
|
12
|
+
"text" => "string",
|
13
|
+
"smallint" => "integer",
|
14
|
+
"integer" => "integer",
|
15
|
+
"bigint" => "integer",
|
16
|
+
"float" => "float",
|
17
|
+
"boolean" => "boolean",
|
18
|
+
"enum" => "string",
|
19
|
+
"json" => "json",
|
20
|
+
"uint8_t" => "integer",
|
21
|
+
"uint16_t" => "integer",
|
22
|
+
"uint32_t" => "integer",
|
23
|
+
"int16_t" => "integer",
|
24
|
+
"int32_t" => "integer",
|
25
|
+
"int64_t" => "integer"
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
|
2
|
+
require 'json'
|
3
|
+
require 'thor/group'
|
4
|
+
require 'apiotics_factory/underscore'
|
5
|
+
|
6
|
+
module ApioticsFactory
|
7
|
+
class Generator < Thor::Group
|
8
|
+
|
9
|
+
include Thor::Actions
|
10
|
+
|
11
|
+
argument :id
|
12
|
+
class_option :library, type: :string, default: "grove_pi"
|
13
|
+
|
14
|
+
desc "Generates an Apiotics driver. Requires the driver name and driver id (from the portal) as arguments."
|
15
|
+
|
16
|
+
def self.source_root
|
17
|
+
Dir.pwd
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch_info
|
21
|
+
config_dir = File.dirname(__FILE__).chomp("/lib/apiotics_factory") + "/var"
|
22
|
+
#puts config_dir
|
23
|
+
if File.exist?(config_dir + "/config.json")
|
24
|
+
puts "Found config file #{config_dir}" + "/config.json"
|
25
|
+
config = JSON.parse(File.read(config_dir + "/config.json"))
|
26
|
+
ApioticsFactory.configuration.public_key = config["public_key"]
|
27
|
+
ApioticsFactory.configuration.private_key = config["private_key"]
|
28
|
+
ApioticsFactory.configuration.portal = config["portal"]
|
29
|
+
else
|
30
|
+
say("Please enter your vendor public key from the Apiotics Portal.")
|
31
|
+
ApioticsFactory.configuration.public_key = ask("Vendor Public Key: ")
|
32
|
+
say("Please enter your vendor private key from the Apiotics Portal.")
|
33
|
+
ApioticsFactory.configuration.private_key = ask("Vendor Private Key: ")
|
34
|
+
say("Please confirm the Apiotics Portal web address.")
|
35
|
+
ApioticsFactory.configuration.portal = ask("Portal: ", default: "https://portal.apiotics.com/")
|
36
|
+
unless ApioticsFactory.configuration.portal[-1] == "/"
|
37
|
+
ApioticsFactory.configuration.portal = ApioticsFactory.configuration.portal + "/"
|
38
|
+
end
|
39
|
+
config = {
|
40
|
+
"public_key" => ApioticsFactory.configuration.public_key,
|
41
|
+
"private_key" => ApioticsFactory.configuration.private_key,
|
42
|
+
"portal" => ApioticsFactory.configuration.portal
|
43
|
+
}
|
44
|
+
#puts config
|
45
|
+
unless Dir.exist?(config_dir)
|
46
|
+
Dir.mkdir(config_dir)
|
47
|
+
end
|
48
|
+
File.write(config_dir + "/config.json", config.to_json)
|
49
|
+
end
|
50
|
+
puts "Fetching driver data from the portal..."
|
51
|
+
data = ApioticsFactory::Portal.driver(id)
|
52
|
+
#puts data
|
53
|
+
data = JSON.parse(data)
|
54
|
+
@driver_name = data["name"]
|
55
|
+
@status = true
|
56
|
+
unless data.keys.include?("errors")
|
57
|
+
@interfaces = data["interfaces"]# a hash with "read", "write" and "all". "read" is an array of read and read/write interfaces. "write" is an array of write and read/write interfaces. "all" is a hash with interface names as the keys.
|
58
|
+
# the value is a hash with an "accessor" key and a "type" key
|
59
|
+
@delay_default = data["delay_default"]
|
60
|
+
if @delay_default == nil
|
61
|
+
@delay_default = Hash.new
|
62
|
+
@interfaces["read"].each do |interface|
|
63
|
+
@delay_default[interface] = 1.0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
else
|
67
|
+
@status = false
|
68
|
+
say("#{data['errors']['detail']}")
|
69
|
+
end
|
70
|
+
@template_path = File.dirname(__FILE__).chomp("/apiotics_factory") + "/templates"
|
71
|
+
end
|
72
|
+
|
73
|
+
#need to account for the case when the portal call fails.
|
74
|
+
|
75
|
+
def create_config_file
|
76
|
+
if @status == true
|
77
|
+
puts "Creating driver config file..."
|
78
|
+
template("#{@template_path}/config.json.erb", "#{@driver_name.downcase}/config.json")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def create_exec_file
|
83
|
+
if @status == true
|
84
|
+
puts "Creating driver executable..."
|
85
|
+
@exec_name = @driver_name
|
86
|
+
@exec_name = @exec_name.gsub(/::/, '/')
|
87
|
+
@exec_name = @exec_name.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
88
|
+
@exec_name = @exec_name.gsub(/([a-z\d])([A-Z])/,'\1_\2')
|
89
|
+
@exec_name = @exec_name.tr("-", "_")
|
90
|
+
@exec_name = @exec_name.downcase
|
91
|
+
template("#{@template_path}/exec.rb.erb", "#{@driver_name.downcase}/rootfs/#{@driver_name.downcase}")
|
92
|
+
chmod "#{@driver_name.downcase}/rootfs/#{@driver_name.downcase}", 0755
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def create_apiotics_driver
|
97
|
+
if @status == true
|
98
|
+
puts "Creating apiotics_driver.rb"
|
99
|
+
template("#{@template_path}/apiotics_driver.rb.erb", "#{@driver_name.downcase}/rootfs/apiotics_driver.rb")
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def create_interface_file
|
104
|
+
if @status == true
|
105
|
+
"Creating #{@exec_name}.rb"
|
106
|
+
template("#{@template_path}/apiotics_interfaces.rb.erb", "#{@driver_name.downcase}/rootfs/#{@exec_name}.rb")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def create_library
|
111
|
+
if @status == true
|
112
|
+
if options[:library] == "grove_pi"
|
113
|
+
puts "Adding GrovePi library..."
|
114
|
+
insert_into_file "#{@driver_name.downcase}/rootfs/#{@exec_name}.rb", "require_relative './grove_pi.rb'\n", :after => "require_relative './apiotics_driver'\n"
|
115
|
+
copy_file "#{@template_path}/grove_pi/grove_pi.rb", "#{@driver_name.downcase}/rootfs/grove_pi.rb"
|
116
|
+
copy_file "#{@template_path}/grove_pi/i2c/i2c.rb", "#{@driver_name.downcase}/rootfs/i2c.rb"
|
117
|
+
copy_file "#{@template_path}/grove_pi/i2c/driver.rb", "#{@driver_name.downcase}/rootfs/i2c/driver.rb"
|
118
|
+
copy_file "#{@template_path}/grove_pi/i2c/driver/i2c-dev.rb", "#{@driver_name.downcase}/rootfs/i2c/driver/i2c-dev.rb"
|
119
|
+
copy_file "#{@template_path}/grove_pi/i2c/driver/gpio.rb", "#{@driver_name.downcase}/rootfs/i2c/driver/gpio.rb"
|
120
|
+
directory "#{@template_path}/grove_pi/i2c/device", "#{@driver_name.downcase}/rootfs/i2c/device"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def create_vendor
|
126
|
+
if @status == true
|
127
|
+
puts "Creating vendor directory..."
|
128
|
+
empty_directory "#{@driver_name.downcase}/rootfs/vendor"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "thor"
|
2
|
+
|
3
|
+
module ApioticsFactory
|
4
|
+
class MyCommand < Thor
|
5
|
+
# register(class_name, subcommand_alias, usage_list_string, description_string)
|
6
|
+
register(ApioticsFactory::Generator, "generate", "generate <DriverId>", "Generate Apiotics Driver. You can find the Id for your driver on the portal. If your driver does not yet exist on the portal, create it there first.")
|
7
|
+
|
8
|
+
# register(class_name, subcommand_alias, usage_list_string, description_string)
|
9
|
+
register(ApioticsFactory::Publish, "publish", "publish <DriverId>", "Publish Apiotics Driver. You can find the Id for your driver on the portal. If your driver does not yet exist on the portal, create it there first.")
|
10
|
+
|
11
|
+
# register(class_name, subcommand_alias, usage_list_string, description_string)
|
12
|
+
register(ApioticsFactory::ClearCredentials, "clear_credentials", "clear_credentials", "Delete vendor and portal information from the Gem.")
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
require 'rest-client'
|
4
|
+
#require 'resolv-replace'
|
5
|
+
|
6
|
+
module ApioticsFactory
|
7
|
+
class Portal
|
8
|
+
|
9
|
+
def self.driver(driver)
|
10
|
+
puts "portal: #{ApioticsFactory.configuration.portal}"
|
11
|
+
puts "public_key: #{ApioticsFactory.configuration.public_key}"
|
12
|
+
puts "private_key: #{ApioticsFactory.configuration.private_key}"
|
13
|
+
puts "id: #{driver}"
|
14
|
+
json = HTTParty.post("#{ApioticsFactory.configuration.portal}api/driver", :query => {:public_key => ApioticsFactory.configuration.public_key, :private_key => ApioticsFactory.configuration.private_key, driver: driver}).body
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.driver_version(driver)
|
18
|
+
json = HTTParty.post("#{ApioticsFactory.configuration.portal}api/driver_version", :query => {:public_key => ApioticsFactory.configuration.public_key, :private_key => ApioticsFactory.configuration.private_key, driver: driver}).body
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.publish_driver(driver, path)
|
22
|
+
payload = {
|
23
|
+
:multipart => true,
|
24
|
+
:file => File.open(path, 'rb'),
|
25
|
+
:public_key => ApioticsFactory.configuration.public_key,
|
26
|
+
:private_key => ApioticsFactory.configuration.private_key,
|
27
|
+
:driver => driver
|
28
|
+
}
|
29
|
+
r = RestClient.post("#{ApioticsFactory.configuration.portal}api/upload_driver", payload)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
|
2
|
+
require 'json'
|
3
|
+
require 'thor/group'
|
4
|
+
|
5
|
+
module ApioticsFactory
|
6
|
+
class Publish < Thor::Group
|
7
|
+
|
8
|
+
include Thor::Actions
|
9
|
+
|
10
|
+
argument :id
|
11
|
+
class_option :path
|
12
|
+
|
13
|
+
desc "Publishes an Apiotics driver. Requires the driver name and driver id (from the portal) as arguments."
|
14
|
+
|
15
|
+
def self.source_root
|
16
|
+
if :path == nil
|
17
|
+
Dir.pwd
|
18
|
+
else
|
19
|
+
:path
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch_info
|
24
|
+
config_dir = File.dirname(__FILE__).chomp("/lib/apiotics_factory") + "/var"
|
25
|
+
|
26
|
+
if File.exist?(config_dir + "/config.json")
|
27
|
+
config = JSON.parse(File.read(config_dir + "/config.json"))
|
28
|
+
ApioticsFactory.configuration.public_key = config["public_key"]
|
29
|
+
ApioticsFactory.configuration.private_key = config["private_key"]
|
30
|
+
ApioticsFactory.configuration.portal = config["portal"]
|
31
|
+
else
|
32
|
+
say("Please enter your vendor public key from the Apiotics Portal.")
|
33
|
+
ApioticsFactory.configuration.public_key = ask("Vendor Public Key: ")
|
34
|
+
say("Please enter your vendor private key from the Apiotics Portal.")
|
35
|
+
ApioticsFactory.configuration.private_key = ask("Vendor Private Key: ")
|
36
|
+
say("Please confirm the Apiotics Portal web address.")
|
37
|
+
ApioticsFactory.configuration.portal = ask("Portal: ", default: "https://portal.apiotics.com/")
|
38
|
+
unless ApioticsFactory.configuration.portal[-1] == "/"
|
39
|
+
ApioticsFactory.configuration.portal = ApioticsFactory.configuration.portal + "/"
|
40
|
+
end
|
41
|
+
config = {
|
42
|
+
"public_key" => ApioticsFactory.configuration.public_key,
|
43
|
+
"private_key" => ApioticsFactory.configuration.private_key,
|
44
|
+
"portal" => ApioticsFactory.configuration.portal
|
45
|
+
}
|
46
|
+
unless Dir.exist?(config_dir)
|
47
|
+
Dir.mkdir(config_dir)
|
48
|
+
end
|
49
|
+
File.write(config_dir + "/config.json", config.to_json)
|
50
|
+
end
|
51
|
+
data = ApioticsFactory::Portal.driver_version(id)
|
52
|
+
#puts data
|
53
|
+
data = JSON.parse(data)
|
54
|
+
@driver_name = data["name"]
|
55
|
+
@status = true
|
56
|
+
unless data.keys.include?("errors")
|
57
|
+
@version = data["version"]
|
58
|
+
else
|
59
|
+
@status = false
|
60
|
+
say("#{data['errors']['detail']}")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
#need to account for the case where the portal call fails.
|
65
|
+
|
66
|
+
def publish
|
67
|
+
if @status == true
|
68
|
+
run("tar -cvzf #{@driver_name.downcase}-#{@version}.tgz ./#{@driver_name.downcase}")
|
69
|
+
say("Uploading driver to the portal...")
|
70
|
+
path = Dir.pwd + "/#{@driver_name.downcase}-#{@version}.tgz"
|
71
|
+
response = ApioticsFactory::Portal.publish_driver(id, path)
|
72
|
+
say("Cleaning up...")
|
73
|
+
run("rm #{@driver_name.downcase}-#{@version}.tgz")
|
74
|
+
if response.code == 200
|
75
|
+
say("Upload successful")
|
76
|
+
else
|
77
|
+
say("Upload not successful")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "apiotics_factory/configuration"
|
2
|
+
require "apiotics_factory/version"
|
3
|
+
require "apiotics_factory/portal"
|
4
|
+
require "apiotics_factory/underscore"
|
5
|
+
require "apiotics_factory/generator"
|
6
|
+
require "apiotics_factory/publish"
|
7
|
+
require "apiotics_factory/clear_credentials"
|
8
|
+
require "apiotics_factory/my_command"
|
9
|
+
|
10
|
+
module ApioticsFactory
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :configuration
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configuration
|
17
|
+
@configuration ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.reset
|
21
|
+
@configuration = Configuration.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.configure
|
25
|
+
yield(configuration)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
class ApioticsDriver
|
2
|
+
require 'socket'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
attr_accessor :debug_enable
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
if File.file?("driver_config.json")
|
9
|
+
@config = JSON.parse(File.read("driver_config.json"))
|
10
|
+
if @config.keys.include?("log_file")
|
11
|
+
$stdout = File.open( @config["log_file"], 'a' )
|
12
|
+
$stdout.sync = true
|
13
|
+
end
|
14
|
+
if @config.keys.include?("driver_name")
|
15
|
+
@driver_name = @config["driver_name"]
|
16
|
+
else
|
17
|
+
@driver_name = "Unspecified"
|
18
|
+
end
|
19
|
+
puts "initializing #{@driver_name}"
|
20
|
+
@socket_address = "localhost"
|
21
|
+
@socket_port = 8001
|
22
|
+
@debug_enable = false
|
23
|
+
@delay = <%= @delay_default %>
|
24
|
+
@threads = {}
|
25
|
+
@state = {}
|
26
|
+
@read_interfaces = <%= @interfaces["read"] %>
|
27
|
+
@write_interfaces = <%= @interfaces["write"] %>
|
28
|
+
@interfaces = <%= @interfaces["all"] %>
|
29
|
+
@interface_kinds = {
|
30
|
+
"string" => "string",
|
31
|
+
"text" => "string",
|
32
|
+
"smallint" => "integer",
|
33
|
+
"integer" => "integer",
|
34
|
+
"bigint" => "integer",
|
35
|
+
"float" => "float",
|
36
|
+
"boolean" => "boolean",
|
37
|
+
"enum" => "string",
|
38
|
+
"json" => "json",
|
39
|
+
"uint8_t" => "integer",
|
40
|
+
"uint16_t" => "integer",
|
41
|
+
"uint32_t" => "integer",
|
42
|
+
"int16_t" => "integer",
|
43
|
+
"int32_t" => "integer",
|
44
|
+
"int64_t" => "integer"
|
45
|
+
}
|
46
|
+
@connected = false
|
47
|
+
@msg_queue = []
|
48
|
+
@flush_active = false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def send_msg(value)
|
53
|
+
interface = caller_locations(1,1)[0].label
|
54
|
+
if interface[0..5] == "write_"
|
55
|
+
interface = interface[6..-1]
|
56
|
+
elsif interface[0..4] == "read_"
|
57
|
+
interface = interface[5..-1]
|
58
|
+
end
|
59
|
+
unless interface == "port"
|
60
|
+
@state[interface] = value
|
61
|
+
else
|
62
|
+
@state[interface] = Kernel.const_get(value)
|
63
|
+
end
|
64
|
+
msg = {
|
65
|
+
"action" => "set-complete",
|
66
|
+
"driver" => @driver_name,
|
67
|
+
"interface" => {
|
68
|
+
interface => value
|
69
|
+
}
|
70
|
+
}
|
71
|
+
self.deliver_msg(msg)
|
72
|
+
end
|
73
|
+
|
74
|
+
def send_msg2(interface, value)
|
75
|
+
if interface[0..5] == "write_"
|
76
|
+
interface = interface[6..-1]
|
77
|
+
elsif interface[0..4] == "read_"
|
78
|
+
interface = interface[5..-1]
|
79
|
+
end
|
80
|
+
unless interface == "port"
|
81
|
+
@state[interface] = value
|
82
|
+
else
|
83
|
+
@state[interface] = Kernel.const_get(value)
|
84
|
+
end
|
85
|
+
msg = {
|
86
|
+
"action" => "set-complete",
|
87
|
+
"driver" => @driver_name,
|
88
|
+
"interface" => {
|
89
|
+
interface => value
|
90
|
+
}
|
91
|
+
}
|
92
|
+
self.deliver_msg(msg)
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_msg
|
96
|
+
puts "Starting get message..."
|
97
|
+
#begin
|
98
|
+
Thread::abort_on_exception = true
|
99
|
+
Thread.new do
|
100
|
+
if @connected == false
|
101
|
+
@socket = TCPSocket.open( @socket_address, @socket_port )
|
102
|
+
connect_msg = {
|
103
|
+
"action" => "connect",
|
104
|
+
"driver" => @driver_name
|
105
|
+
}
|
106
|
+
puts "Sending #{connect_msg.to_json}..."
|
107
|
+
@socket.puts connect_msg.to_json
|
108
|
+
@connected = true
|
109
|
+
end
|
110
|
+
while response = @socket.gets.chomp
|
111
|
+
if @debug_enable == true
|
112
|
+
puts "Received message #{response}..."
|
113
|
+
end
|
114
|
+
msg = JSON.parse(response)
|
115
|
+
msg = msg["interface"]
|
116
|
+
if msg != nil
|
117
|
+
msg.each do |k,v|
|
118
|
+
if k != "delay" && k != "port" && @write_interfaces.include?(k)
|
119
|
+
method = "write_#{k}"
|
120
|
+
v = self.type_cast(k,v)
|
121
|
+
if @interfaces.keys.include?("port")
|
122
|
+
unless @state["port"] == nil
|
123
|
+
self.send(method, v)
|
124
|
+
else
|
125
|
+
self.msg_queue(method, v)
|
126
|
+
end
|
127
|
+
else
|
128
|
+
self.send(method, v)
|
129
|
+
end
|
130
|
+
elsif k == "delay"
|
131
|
+
if v.is_a?(String)
|
132
|
+
v = JSON.parse(v)
|
133
|
+
end
|
134
|
+
v.each do |key, val|
|
135
|
+
@delay[key] = val.to_f
|
136
|
+
self.send_msg2(k,v)
|
137
|
+
end
|
138
|
+
else
|
139
|
+
self.send_msg2(k,v)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
#rescue => e
|
146
|
+
# puts e.message
|
147
|
+
# puts e.backtrace
|
148
|
+
# get_msg
|
149
|
+
#end
|
150
|
+
end
|
151
|
+
|
152
|
+
def msg_queue(method, v)
|
153
|
+
array = [method, v]
|
154
|
+
@msg_queue << array
|
155
|
+
if @flush_active == false
|
156
|
+
self.flush_queue
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def flush_queue
|
161
|
+
Thread::abort_on_exception = true
|
162
|
+
Thread.new do
|
163
|
+
@flush_active = true
|
164
|
+
while @msg_queue.length > 0
|
165
|
+
entry = @msg_queue[0]
|
166
|
+
if @state["port"] != nil
|
167
|
+
self.send(entry[0], entry[1])
|
168
|
+
@msg_queue = @msg_queue - [entry]
|
169
|
+
end
|
170
|
+
sleep 0.1
|
171
|
+
end
|
172
|
+
@flush_active = false
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def read_msg
|
177
|
+
puts "starting read message"
|
178
|
+
@delay.each do |k,v|
|
179
|
+
unless @threads.keys.include?(k) || k == "port" || k == "delay"
|
180
|
+
#begin
|
181
|
+
Thread::abort_on_exception = true
|
182
|
+
t = Thread.new do
|
183
|
+
while true
|
184
|
+
unless @state["port"] == nil
|
185
|
+
read_function = "read_#{k}"
|
186
|
+
self.send(read_function)
|
187
|
+
sleep @delay[k]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
#rescue => e
|
192
|
+
# if @debug_enable == true
|
193
|
+
# puts e.message
|
194
|
+
# puts e.backtrace
|
195
|
+
# end
|
196
|
+
# @threads.delete(k)
|
197
|
+
# read_msg
|
198
|
+
#end
|
199
|
+
@threads[k] = t
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
def close
|
206
|
+
msg = {
|
207
|
+
"action" => "disconnect"
|
208
|
+
}
|
209
|
+
msg = msg.to_json
|
210
|
+
self.deliver_msg(msg)
|
211
|
+
end
|
212
|
+
|
213
|
+
def type_cast(k, v)
|
214
|
+
if @interfaces[k]["type"] == "boolean"
|
215
|
+
if v.is_a?(String)
|
216
|
+
if v == "true"
|
217
|
+
v = true
|
218
|
+
elsif v == "false"
|
219
|
+
v = false
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
if @interfaces[k]["type"] == "float"
|
224
|
+
if v.is_a?(String)
|
225
|
+
v = v.to_f
|
226
|
+
end
|
227
|
+
end
|
228
|
+
if @interface_kinds[@interfaces[k]["type"]] == "integer"
|
229
|
+
if v.is_a?(String)
|
230
|
+
v = v.to_i
|
231
|
+
end
|
232
|
+
end
|
233
|
+
return v
|
234
|
+
end
|
235
|
+
|
236
|
+
def deliver_msg(msg)
|
237
|
+
if @debug_enable == true
|
238
|
+
puts "Sending Message: #{msg}\n"
|
239
|
+
end
|
240
|
+
jmsg = msg.to_json
|
241
|
+
@socket.puts(jmsg)
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative './apiotics_driver'
|
2
|
+
class <%= @driver_name %> < ApioticsDriver
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
super
|
6
|
+
self.get_msg
|
7
|
+
sleep 5
|
8
|
+
self.read_msg
|
9
|
+
end
|
10
|
+
|
11
|
+
# the @state variable is available to you. For each interface you have, you can get it's current
|
12
|
+
# value by reading @state["interface_name"].
|
13
|
+
<% @interfaces["all"].each do |k, v|%>
|
14
|
+
<% unless k == "port" || k == "delay" %>
|
15
|
+
<% if v["accessor"] == "write only" || v["accessor"] == "read/write" %>
|
16
|
+
def <%= "write_#{k}(#{v["type"]})"%>
|
17
|
+
|
18
|
+
# Your hardware specific logic to set the state of your <%= k %> hardware goes here based on the value of the <%= v['type'] %> variable.
|
19
|
+
|
20
|
+
self.send_msg(<%= v["type"]%>) # This line sends a confirmation message back to the web application noting that the operation is complete.
|
21
|
+
end
|
22
|
+
<% end %>
|
23
|
+
<% if v["accessor"] == "read only" || v["accessor"] == "read/write" %>
|
24
|
+
def <%= "read_#{k}"%>
|
25
|
+
|
26
|
+
# Your hardware specific logic here should read a value from your <%= k %> hardware, and set the <%= v["type"] %> variable appropriately.
|
27
|
+
|
28
|
+
self.send_msg(<%= v["type"]%>) # This line sends the value in the <%= v["type"] %> variable to the web application.
|
29
|
+
end
|
30
|
+
<% end %>
|
31
|
+
<% end %>
|
32
|
+
<% end %>
|
33
|
+
end
|