Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions NodeRed-Butter
Submodule NodeRed-Butter added at 2be5c3
Binary file added nodes/__pycache__/api.cpython-39.pyc
Binary file not shown.
60 changes: 60 additions & 0 deletions nodes/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import json
from typing import Literal
import lxml
from kafka import KafkaProducer, KafkaConsumer


class TestObject:
def __init__(self, name: str, distance: int, angle: int):
self.name = name
self.distance = distance
self.angle = angle

class KafkaAPI:
@staticmethod
def connect_kafka_producer() -> KafkaProducer:
_producer = None
try:
_producer = KafkaProducer(bootstrap_servers=['192.168.57.12:9092'])
except Exception as ex:
print('Exception while connecting Kafka')
print(str(ex))
finally:
return _producer

@staticmethod
def publish_message(producer_instance: KafkaProducer, topic_name: str, key: str, value: str):
try:
key_bytes = bytes(key, encoding='utf-8')
value_bytes = bytes(value, encoding='utf-8')
producer_instance.send(topic_name, key=key_bytes, value=value_bytes)
producer_instance.flush()
#Sprint('Message published successfully.')
except Exception as ex:
print('Exception in publishing message')
print(str(ex))

@staticmethod
def Publish(topic: str, time: int, data: object):
kafka_producer = KafkaAPI.connect_kafka_producer()
newDict = data.__dict__
newDict["time"] = time
KafkaAPI.publish_message(kafka_producer, topic, 'raw', json.dumps(newDict))
if kafka_producer is not None:
kafka_producer.close()

@staticmethod
def Subscribe(topic: str, duration: int, offset: Literal['earliest', 'latest'], callback: any):
consumer = KafkaConsumer(topic, auto_offset_reset=offset,
bootstrap_servers=['192.168.57.12:9092'], consumer_timeout_ms=duration)
for msg in consumer:
newDataPoint = json.loads(msg.value)
callback(newDataPoint)
if consumer is not None:
consumer.close()

if __name__ == '__main__':
newDataPoint = TestObject("gazer", 5, 90)
KafkaAPI.Publish('topic3', 34, newDataPoint)
KafkaAPI.Subscribe('topic3', 3000, 'earliest', print)

133 changes: 133 additions & 0 deletions nodes/get-distance.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<!-- Set Motor Register Node -->

<script type="text/javascript">
RED.nodes.registerType('get-distance',{
category: 'butter',
color: '#5c6bc0',
defaults: {
name: {
value: "get-distance",
required: true,
},
robotIp: {
value: "0.0.0.0",
},
motorName: {
value: "",
},
registerName: {
value: "",
},
value: {
value: 0,
},
debugMode: {
value: false
}
},
inputs:1,
outputs:1,
icon: "icons/butter-logo-white.png",
paletteLabel: "get-distance",
label: function() {
return this.name||"get-distance";
},
align: 'right',
outputLabels: ["operation result"],
inputLabels: ["op instructions (optional)"]
});
</script>

<script type="text/html" data-template-name="get-distance">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
<div class="form-row">
<label for="node-input-robotIp"><i class="fa fa-tag"></i> Robot IP</label>
<input type="text" id="node-input-robotIp" placeholder="0.0.0.0">
</div>
<div class="form-row">
<label for="node-input-motorName"><i class="fa fa-tag"></i> Motor Name</label>
<input type="text" id="node-input-motorName" placeholder="motor name here">
</div>
<div class="form-row">
<label for="node-input-registerName"><i class="fa fa-tag"></i> Register Name</label>
<select id="node-input-registerName">
<option value="return-level">return-level</option>
<option value="upper-limit-voltage">upper-limit-voltage</option>
<option value="multi-turn-offset">multi-turn-offset</option>
<option value="led">led</option>
<option value="moving-speed">moving-speed</option>
<option value="max-torque">max-torque</option>
<option value="punch">punch</option>
<option value="goal-acceleration">goal-acceleration</option>
<option value="goal-position">goal-position</option>
<option value="lower-limit-voltage">lower-limit-voltage</option>
<option value="id">id</option>
<option value="return-delay-time">return-delay-time</option>
<option value="baud-rate">baud-rate</option>
<option value="ccw-angle-limit">ccw-angle-limit</option>
<option value="torque-limit">torque-limit</option>
<option value="d-gain">d-gain</option>
<option value="limit-temprature">limit-temprature</option>
<option value="resolution-driver">resolution-driver</option>
<option value="cw-angle-limit">cw-angle-limit</option>
<option value="i-gain">i-gain</option>
<option value="alarm-shutdown">alarm-shutdown</option>
<option value="torque-enable">torque-enable</option>
<option value="p-gain">p-gain</option>
<option value="alarm-led">alarm-led</option>
</select>
</div>
<div class="form-row">
<label for="node-input-value"><i class="fa fa-tag"></i> Value</label>
<input type="number" id="node-input-value">
</div>
<div class="form-row">
<label for="node-input-debugMode"><i class="fa fa-tag"></i> Debug Mode</label>
<input type="checkbox" id="node-input-debugMode" placeholder="">
</div>
<div class="form-tips"><b>Note !</b> If the node receives as input a json with the above properties (not including name and debug mode properties), the node will disregard the ones provided in the UI.
See the help section to understand how this json should be composed.</div>
<div class="form-tips"><b>Tip:</b> Save the robotIp property as a `flow variable` for reuse.</div>
</script>

<script type="text/html" data-help-name="set-motor-register">
<p>Sets a register to a value in a specific motor.</p>
<h3>Inputs</h3>
<ol class="node-ports">
<li>parameters (Optional)
<dl class="message-properties">
<dt>payload <span class="property-type">json object</span></dt>
<dd>The payload has to be of the following format: <br>
<code>
{ <br>
"robotIp": some-robot-ip, <br>
"motorName": some-motor-name, <br>
"registerName": some-register-name, <br>
"value": some-register-value, <br>
}
</code>
</dd>
</dl>
</li>
</ol>
<h3>Outputs</h3>
<ol class="node-ports">
<li>Operation Result
<dl class="message-properties">
<dt>payload <span class="property-type">string</span></dt>
<dd>the result of the butter command.</dd>
</dl>
</li>
</ol>

<h3>Details</h3>
<p><code>node.robotIp</code> is used to indicate the robot's ip to play animation on.
<p><code>node.motorName</code> is used to indicate which motor to manipulate.</p>
<p><code>node.registerName</code> is used to indicate which motor register to write to.</p>
<p><code>node.value</code> is used to indicate which integer value to write to the register.</p>
<p><code>node.debugMode</code> if true, the node will print logs to the debug console.</p>
<p>make sure that the robot is indeed running and the animation was deployed successfully before running the node.</p>
</script>
117 changes: 117 additions & 0 deletions nodes/get-distance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@

module.exports = function(RED) {

var express = require("express");
const axios = require("axios");





function get_distance(req, res) {
console.log("kkkkkllkk");
var spawn = require("child_process").spawn;

// E.g : http://localhost:3000/name?firstname=Mike&lastname=Will
// so, first name = Mike and last name = Will
var process = spawn("python", [
"/home/kafka/Desktop/new-butter/NodeRed-Butter/nodes/sensors.py",
req.query.firstname,
req.query.lastname
])

process.stdout.on("data", function(data) {
// console.log(data.toString());
res.send(data.toString());
})
}


var InitServer = (function () {
var app;

function createInstance() {
var app = express();
return app;
}

return {
getInstance: function () {
if (!app) {
app = createInstance();
}
return app;
}
};
})();

function GetDistNode(config) {
RED.nodes.createNode(this, config);
this.config = config;

const DebugLogger = require('../logger/debug_logger');
//const butterClientProvider = require('../butter-client/butter-client-provider');

//this.butterHttpClient = butterClientProvider.GetClient(this.config.robotIp);
this.debugLogger = new DebugLogger(this, this.config.debugMode);

app = InitServer.getInstance();
app.listen(3004, function() {
console.log("server running on port 3004");
});
app.get("/name", get_distance);


this.on('input', async function(msg) {
// let robotIp = this.config.robotIp;
// let motorName = this.config.motorName;
// let registerName = this.config.registerName;
// let value = this.config.value;

// // check if message has correct json payload - if yes run it instead.
// if (
// msg.payload.robotIp != undefined &&
// msg.payload.motorName != undefined &&
// msg.payload.registerName != undefined &&
// msg.payload.value != undefined
// ) {
// robotIp = msg.payload.robotIp;
// motorName = msg.payload.motorName;
// registerName = msg.payload.registerName;
// value = msg.payload.value;
// }

// sets the motor.
// this.debugLogger.logIfDebugMode(
// `setting the register ${registerName} of motor ${motorName} of robot ${robotIp} to ${value}`
// );

try {
//butter_response = await this.butterHttpClient.setMotorRegister(motorName, registerName, value);
console.log("ILOVEBeny");
axios.get("http://127.0.0.1:3004/name?firstname=Omer&lastname=Noam").then(response => {
console.log(response.data);
//console.log(JSON.stringify(response));

this.send({ payload: response.data });
this.debugLogger.logIfDebugMode(`butter response is ${response.data.toString()}`);

})
.catch(error => {
console.log(error);
});


} catch (error) {
console.log(error);

this.debugLogger.logIfDebugMode(error);

this.debugLogger.logIfDebugMode(`failed to get distance`);
}
});
}

// register node type.
RED.nodes.registerType('get-distance', GetDistNode);
};
16 changes: 16 additions & 0 deletions nodes/sensors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import sys
from api import KafkaAPI
from api import TestObject

cur_dist = []

def RET(msg):
cur_dist.append(msg['distance'])


newDataPoint = TestObject("gazer", 5, 90)
KafkaAPI.Publish('topic3', 34, newDataPoint)
KafkaAPI.Subscribe('topic3', 3000, 'earliest', RET)

print(cur_dist[0])
sys.stdout.flush()
14 changes: 11 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"butter-play-animation": "nodes/play-animation.js",
"butter-set-motor-register": "nodes/set-motor-register.js",
"butter-get-motor-register": "nodes/get-motor-register.js",
"butter-get-available-animations": "nodes/get-available-aimations.js"
"butter-get-available-animations": "nodes/get-available-aimations.js",
"get-distance": "nodes/get-distance.js"
}
},
"scripts": {
Expand All @@ -18,15 +19,22 @@
"type": "git",
"url": "git+https://github.com/idc-milab/NodeRed-Butter.git"
},
"keywords": [ "butter", "robotics", "mas", "node-red", "milab" ],
"keywords": [
"butter",
"robotics",
"mas",
"node-red",
"milab"
],
"author": "Ron Gissin",
"license": "MIT",
"bugs": {
"url": "https://github.com/idc-milab/NodeRed-Butter/issues"
},
"homepage": "https://github.com/idc-milab/NodeRed-Butter#readme",
"dependencies": {
"@butter-robotics/mas-javascript-api": "^0.13.0"
"@butter-robotics/mas-javascript-api": "^0.13.0",
"express": "^4.18.1"
},
"devDependencies": {
"jest": "^26.6.3",
Expand Down