Compare commits

10 Commits

8 changed files with 936 additions and 55 deletions
+7 -1
View File
@@ -1,7 +1,7 @@
// =============================================================================
// Auth: Alex Celani
// File: colonel.go
// Revn: 06-27-2022 1.2
// Revn: 07-05-2022 2.0
// Func: Send message to another machine and then receive a single
// response. Extremely tight bounds, not robust at all
//
@@ -13,6 +13,7 @@
//*06-20-2022: changed name to reflect first draft of colonel.go
// 06-23-2022: made input an infinite loop
// 06-27-2022: added flag package, assumed ip
//*07-05-2022: added support for kill messages
//
// =============================================================================
@@ -90,6 +91,11 @@ func main() {
if *verbose {
fmt.Println( "\nrtt: ", t.Sub( start ) ) // print time
}
// response is "kill" if user sends a kill message
if string( buf[:n] ) == "kill" {
os.Exit( 1 )
}
}
os.Exit( 0 ) // exeunt
+22 -29
View File
@@ -1,10 +1,11 @@
// =============================================================================
// Auth: Alex Celani
// File: led.go
// Revn: 07-04-2022 2.0
// Revn: 07-11-2022 4.0
// Func: receive data as an endpoint, send response back to middleman
// ONLY A SIMULATION FOR THE REAL THING
//
// TODO:
// TODO: implement rainbow
// =============================================================================
// CHANGE LOG
// -----------------------------------------------------------------------------
@@ -28,6 +29,8 @@
// added support for white, cyan, yellow, and magenta
// finished comments for tell() and ask() and even simple
// parsing stuff in handleClient()
//*07-05-2022: added support for kill messages
//*07-11-2022: added /list/ as field that calls ask()
//
// =============================================================================
@@ -86,24 +89,6 @@ func ask( comm []string ) string {
}
/*
// quick function to make a rainbow
func rainbow() {
for {
for r := 255; r >= 0; r-- {
}
for g := 255; g >= 0; g-- {
}
for b := 255; b >= 0; b-- {
}
}
}
*/
// quick function to change LED colors
func tell( comm []string ) string {
@@ -144,9 +129,6 @@ func tell( comm []string ) string {
pinR.Low()
pinG.Low()
pinB.Low()
case "rainbow": // user wants a loop
// go rainbow()
response = "not yet implemented"
default: // user asks for else, bail out
response = "color unknown"
status = "off"
@@ -163,8 +145,9 @@ func tell( comm []string ) string {
default: // user asks for else, bail out
response = "status unknown"
}
case "kill": // user wants to stop running
os.Exit( 2 ) // FIXME
// user wants to stop running
case "kill", "quit", "end", "die", "stop":
response = "kill"
default: // user enters incorrect command
response = "command unknown"
}
@@ -197,12 +180,10 @@ func handleClient( conn net.Conn ) {
// split command into words for ease of parsing
command := strings.Split( string( buf[:n] ), " " )
if command[1] != "led" { // confirm message belongs here
if command[1] != "piled" { // confirm message belongs here
os.Exit( 2 ) // if not, exit
}
// TODO differentiate ask commands from tell commands
// initiate response variable
var resp string
@@ -210,6 +191,9 @@ func handleClient( conn net.Conn ) {
case "ask": // if getting
// get, store response in variable to be sent back
resp = ask( command[2:] )
case "list": // is user getting list?
// get, store response in variable to be sent back
resp = ask( command )
case "tell": // if setting
// set, store response in variable to be sent back
resp = tell( command[2:] )
@@ -230,6 +214,10 @@ func handleClient( conn net.Conn ) {
if *verbose { // XXX identical verbose print
fmt.Println( "sent: ", resp ) // print response
}
if resp == "kill" { // if user sent back kill
os.Exit( 1 ) // end process
}
}
}
@@ -258,7 +246,7 @@ func main() {
lg = flag.Int( "lg", 9, "GPIO pin for green LED" )
lb = flag.Int( "lb", 25, "GPIO pin for blue LED" )
verbose = flag.Bool( "v", false, "verbose printing" )
ip = flag.String( "ip", ":1202", "ip and port of self" )
ip = flag.String( "ip", ":1212", "ip and port of self" )
flag.Parse()
// initialize pins to GPIO 11, 9, and 25
@@ -280,6 +268,11 @@ func main() {
pinG.Output()
pinB.Output()
// Initialize output to "off"
pinR.High()
pinG.High()
pinB.High()
// "resolve" ip & host according to TCP rules
tcpAddr, err := net.ResolveTCPAddr( "tcp", *ip )
check( err ) // check error
+398
View File
@@ -0,0 +1,398 @@
// ===================================================================
// Auth: Alex
// File: led.ino
// Revn: 07-20-2022 1.0
// Func: End node, to run on ESP8266. Operate as server, process
// incoming messages, react to them, send responses
//
// TODO: incorporate interrupt example for builtin led
// ===================================================================
// CHANGE LOG
// -------------------------------------------------------------------
// 07-07-2022: found online
// 07-12-2022: changed client.write() to client.println(), to fix
// bug about sending Strings
// investigated strcmp, pinmode, etc
// cleaned up, commented setup()
// 07-13-2022: commented
// investigated faster ways to take input; no dice
// toggle builtin led on receipt
// 07-14-2022: commented loop()
// wrote prse()
// began added [list esp] message, lst()
// wrote bones for refresh(), to update led
// 07-19-2022: added led to circuit, wrote refresh()
// moved check for /list/ above check for second word
// to fix /list/ not sending bug
// added default error value for response variable
// added call to refresh at the end of setup() to init
// led state
//*07-20-2022: /tell/ commands set status to on
// commented
// changed name of file from democode to led.ino
// changed NAME var from "esp" to "led"
//
// ===================================================================
// ESP8266 version of Wifi.h
// API Documentation located at:
//
// Pinout located at:
// https://microcontrollerslab.com/led-blinking-using-esp8266-nodemcu/#ESP8266_Pinout_in_Arduino_IDE
#include <ESP8266WiFi.h>
// documentation at
// https://arduino.cc/reference/en/libraries/esp8266timerinterrupt/
#include <ESP8266TimerInterrupt.h>
// real shit, not certain why this is important
#define SendKey 0 // button to send data Flash BTN on NodeMCU
int port = 1210; // port of server
WiFiServer server(port);
IPAddress self( 192, 168, 1, 69 ); // ip address of self
IPAddress gate( 192, 168, 1, 1 ); // ip address of gateway
IPAddress subn( 255, 255, 0, 0 ); // subnet mask
IPAddress pDNS( 8, 8, 8, 8 ); // primary DNS server ip
IPAddress sDNS( 8, 8, 4, 4 ); // secondary DNS server ip
// credentials for WiFi Network
const char *ssid = "waystation";
const char *password = "homophobiaR0X";
// pin definitions
int pinR = 16; // GPIO16 --> D0 on the board
int pinG = 5; // GPIO5 --> D1 on the board
int pinB = 4; // GPIO4 --> D2 on the board
//int pinBI = 2; // GPIO2 --> D4 on the board, built-in LED
// state variables
bool statusRGB = true; // init, true -> off
String color = "red"; // init color red
bool statusBI = true; // init, true -> off
String NAME = "led"; // variable to semi-hardcode program name
// ===================================================================
// Power on setup
// ===================================================================
void setup() {
pinMode( pinBI, OUTPUT ); // set built-in LED to output
digitalWrite( pinBI, statusBI ); // initialize it off
// this really dumb fucking board is active low, I am fucking weeping
// init rgb led pins as output
pinMode( pinR, OUTPUT );
pinMode( pinG, OUTPUT );
pinMode( pinB, OUTPUT );
// init rgb led as off ( active low, the headache )
digitalWrite( pinR, HIGH );
digitalWrite( pinG, HIGH );
digitalWrite( pinB, HIGH );
Serial.begin( 115200 ); // board works at 115,200 [baud]
pinMode( SendKey, INPUT_PULLUP ); // Btn to send data (NMC NMM)
WiFi.mode( WIFI_STA ); // set mode to station
// set static ip address ( only way it would let me do it )
WiFi.config( self, gate, subn, pDNS, sDNS );
WiFi.begin( ssid, password ); // connect to wifi
// Serial.prints all considered verbose, because you can only see
// them if you're trying to
// wait for connection
Serial.println( "Connecting to Wifi" );
// iterate until connected
while( WiFi.status() != WL_CONNECTED ) {
delay( 1000 );
Serial.print( "." );
}
// print connection status
Serial.println();
Serial.print( "Connected to " );
Serial.println( ssid );
server.begin(); // bring server online
// print server address
Serial.print( "On at: " );
Serial.print( WiFi.localIP() );
Serial.print( ":" );
Serial.println( port );
refresh(); // update led on setup
}
void refresh() {
// print led status
Serial.print( "color: " );
Serial.println( color );
Serial.print( "status: " );
Serial.println( statusRGB );
// declare all pins off
digitalWrite( pinR, HIGH );
digitalWrite( pinG, HIGH );
digitalWrite( pinB, HIGH );
// if led should be on
// if off, led pins are already off
if( !statusRGB ) {
if( color == "red" ) { // if red
digitalWrite( pinR, LOW ); // turn on only red
} else if( color == "green" ) { // if green
digitalWrite( pinG, LOW ); // turn on only green
} else if( color == "blue" ) { // if blue
digitalWrite( pinB, LOW ); // turn on only blue
} else if( color == "cyan" ) { // if 'cyan'
digitalWrite( pinG, LOW ); // cyan = B + G
digitalWrite( pinB, LOW );
} else if( color == "yellow" ) { // if yellow
digitalWrite( pinR, LOW ); // yellow = R + G
digitalWrite( pinG, LOW );
} else if( color == "magenta" ) { // if magenta
digitalWrite( pinR, LOW ); // magenta = R + B
digitalWrite( pinB, LOW );
} else if( color == "white" ) { // if white
digitalWrite( pinR, LOW ); // all on for white
digitalWrite( pinG, LOW );
digitalWrite( pinB, LOW );
}
}
}
String lst() {
//craft list string and return
String resp = "";
resp = resp + "color ^v\n";
resp = resp + " blue\n";
resp = resp + " cyan\n";
resp = resp + " green\n";
resp = resp + " magenta\n";
resp = resp + " red\n";
resp = resp + " white\n";
resp = resp + " yellow\n";
resp = resp + " status ^v\n";
resp = resp + " on\n";
resp = resp + " off\n";
resp = resp + " off ^\n";
resp = resp + " on ^\n";
resp = resp + " list v";
return resp;
}
// ===================================================================
String prse( String command ) {
int amnt = 0; // keep track of amount of spaces
int len = command.length(); // capture length
// quick variables to keep track of command
bool ask = false;
bool tell = false;
bool list = false;
// init failure string
String response = "error: command unrecognized";
// iterate over each letter of incoming message
for( int i = 0; i < command.length(); i++ ) {
// when letter is space...
if( command[i] == ' ' ) {
amnt++; // increment count for number of spaces
// on the first space
if( amnt == 1 ) {
// print first command
Serial.print( "ask/tell: " );
Serial.println( command.substring( 0, i ) );
// snatch bools for various commands
ask = command.substring( 0, i ) == "ask";
tell = command.substring( 0, i ) == "tell";
list = command.substring( 0, i ) == "list";
// print bools
Serial.println( ask );
Serial.println( tell );
Serial.println( list );
// seemed hacky but it fuckin works
// if command was list, get list and return it
if( list ) {
Serial.println( "LIST" );
response = lst();
Serial.println( response );
}
// on the second space
} else if( amnt == 2 ) {
// capture everything after third word
String argument = command.substring( i + 1, len );
// if user is asking ( should make argument one word )
if( ask ) {
// if user asks for color
if( argument == "color" ) {
response = color; // set response to current color
// if user wants status
} else if( argument == "status" ) {
// if status is true, led is off, else on
response = statusRGB ? "off" : "on";
} else { // doesn't want status or color, arg unknown
response = "field unrecognized";
}
// if user is telling ( some args can be one word )
} else if( tell ) {
// only args can be on and off
if( argument == "off" || argument == "on" ) {
// set status, and set return string
statusRGB = argument == "off";
response = argument;
} else { // if user didn't tell on or off
// print argument
Serial.print( "argument: " );
Serial.println( argument );
// capture number of letters in argument
int arglen = argument.length();
// bool to keep track of if a space was seen
// if there was no space seen, arg is very incorrect
bool space = false;
// iterate over characters in argument
for( int j = 0; j < arglen; j++ ) {
// if letter is a space
if( argument[j] == ' ' ) {
// mark space as true
space = true;
// grab everything before the space, subcommand
String subcom = argument.substring( 0, j );
// grab everything after the space, subargument
String subarg = argument.substring( j + 1, arglen );
// if subcommand is color
if( subcom == "color" ) {
// and subarg matches a supported color
if( subarg == "red" || subarg == "magenta"
|| subarg == "blue" || subarg == "cyan"
|| subarg == "green" || subarg == "yellow"
|| subarg == "white" ) {
// set color to the input color
color = subarg;
// set response to the same color
response = color;
// turn on led
statusRGB = false;
} else { // if color isn't supported
// say so, don't change anything
response = "argument unrecognized";
}
// if subcommand is for status
} else if( subcom == "status" ) {
// if subarg is for either on or off
if( subarg == "off" || subarg == "on" ) {
// set status to input
statusRGB = subarg == "off";
// if status is true, led is off, else on
response = statusRGB ? "off" : "on";
} else { // if subarg isn't on or off
// say so, don't change anything
response = "argument unrecognized";
}
} else { // command is neither color nor status
// say so, don't change anything
response = "command unrecognized";
}
// break out once space is found
// no need to keep iterating once space is found
break;
}
}
// if space was never found, argument was very wrong
if( !space ) {
// say so, don't change anything
response = "/" + argument + "/ invalid";
}
}
} else { // if user is neither asking nor telling
// also not list
// say so, don't change anything
response = "command unrecognized";
}
}
}
}
refresh(); // update the led
// print response
Serial.print( "resp IN:" );
Serial.println( response );
return response; // return response
}
// ===================================================================
// Loop
// ===================================================================
void loop() {
// returns client one exists
WiFiClient client = server.available();
if( client ) { // if server.available() finds client
if( client.connected() ) { // client has connected
Serial.println( "Client Connected" );
}
String response = ""; // init response variable
// for as long as client stays connected
while( client.connected() ) {
// for as long as there are bytes to be read, read em
while( client.available() > 0 ) {
// read data from the connected client
String command = client.readStringUntil( '\n' );
//digitalWrite( pinBI, statusBI = !statusBI );
Serial.println( command ); // print to serial monitor
int count = 0;
int previous = 0; // keep track of spaces
// iterate over string
for( int i = 0; i <= command.length(); i++ ) {
// if char is space or end of string
if( command[i] == ' ' || command[i] == '\0') {
// print each word in command (except last one)
Serial.println( command.substring( previous, i ) );
if( count == 1 ) { // check second word for name
// if command was meant for me...
if( command.substring( previous, i ) == NAME ) {
// parse command
response = prse( command );
Serial.print( "resp OUT:" );
Serial.println( response );
//response = "not just yet";
} else { // tell user message doesn't belong here
// this shouldn't be possible, but
response = "node mismatch";
}
// once response is set, leave loop and send
break;
}
// set previous to AFTER space, to capture ONLY the word
previous = i + 1;
count++; // increment count of spaces
}
}
// print rest of message to monitor
Serial.println( command.substring( previous, command.length() ) );
client.println( response ); // send entire command to client
}
}
client.stop(); // stop client when done reading
Serial.println( "Client disconnected" ); // print
}
}
//=======================================================================
+4
View File
@@ -0,0 +1,4 @@
status ^v
on
off
list v
+6 -2
View File
@@ -1,7 +1,11 @@
color ^v
red
green
blue
cyan
green
magenta
red
white
yellow
status ^v
on
off
+4
View File
@@ -0,0 +1,4 @@
piled|:1212
led2|:1203
led|192.168.1.69:1210
range|192.168.1.30:1211
+364
View File
@@ -0,0 +1,364 @@
// ===================================================================
// Auth: Alex
// File: led.ino
// Revn: 07-27-2022 1.0
// Func: End node, to run on ESP8266. Operate as server, process
// incoming messages, react to them, send responses
//
// TODO: incorporate interrupt example for builtin led
// update to match lst()
// ===================================================================
// CHANGE LOG
// -------------------------------------------------------------------
// 07-25-2022: pulled from led.ino
// 07-26-2022: added /fill/ and units to /fill/ and /dist/
// made distance calc values into constants
// comments
// rewrote lst() a little bit
// 07-27-2022: made quick getDepth() function to fix bad unit bug
// filled in /tell/
// commented
//
// ===================================================================
// ESP8266 version of Wifi.h
// API Documentation located at:
//
// Pinout located at:
// https://microcontrollerslab.com/led-blinking-using-esp8266-nodemcu/#ESP8266_Pinout_in_Arduino_IDE
#include <ESP8266WiFi.h>
// documentation at
// https://arduino.cc/reference/en/libraries/esp8266timerinterrupt/
#include <ESP8266TimerInterrupt.h>
// real shit, not certain why this is important
#define SendKey 0 // button to send data Flash BTN on NodeMCU
#define Vs 0.034 // speed of sound [cm/s]
#define CMtoIN 0.393701 // convert cm to inches [in/cm]
int port = 1211; // port of server
WiFiServer server(port);
IPAddress self( 192, 168, 1, 30 ); // ip address of self
IPAddress gate( 192, 168, 1, 1 ); // ip address of gateway
IPAddress subn( 255, 255, 0, 0 ); // subnet mask
IPAddress pDNS( 8, 8, 8, 8 ); // primary DNS server ip
IPAddress sDNS( 8, 8, 4, 4 ); // secondary DNS server ip
// credentials for WiFi Network
const char *ssid = "waystation";
const char *password = "homophobiaR0X";
// pin definitions
int trig = 10; // GPIO10 --> SDD3 on the board
int echo = 9; // GPIO9 --> SDD2 on the board
// state variables
// permanently set to [cm]
int depth = 30; // depth of cat food container [cm]
bool inch = false; // keep track of units
String NAME = "range"; // variable to semi-hardcode program name
// ===================================================================
// Power on setup
// ===================================================================
void setup() {
// this really dumb fucking board is active low, I am fucking weeping
// init rgb led pins as output
pinMode( trig, OUTPUT );
pinMode( echo, INPUT );
Serial.begin( 115200 ); // board works at 115,200 [baud]
pinMode( SendKey, INPUT_PULLUP ); // Btn to send data (NMC NMM)
WiFi.mode( WIFI_STA ); // set mode to station
// set static ip address ( only way it would let me do it )
WiFi.config( self, gate, subn, pDNS, sDNS );
WiFi.begin( ssid, password ); // connect to wifi
// Serial.prints all considered verbose, because you can only see
// them if you're trying to
// wait for connection
Serial.println( "Connecting to Wifi" );
// iterate until connected
while( WiFi.status() != WL_CONNECTED ) {
delay( 1000 );
Serial.print( "." );
}
// print connection status
Serial.println();
Serial.print( "Connected to " );
Serial.println( ssid );
server.begin(); // bring server online
// print server address
Serial.print( "On at: " );
Serial.print( WiFi.localIP() );
Serial.print( ":" );
Serial.println( port );
}
String lst() {
//craft list string and return
String resp = "";
resp = resp + "dist v\n";
resp = resp + " fill v\n";
resp = resp + " depth ^v\n";
resp = resp + " /int/\n";
resp = resp + " unit ^v\n";
resp = resp + " in[ch]\n";
resp = resp + " cm";
return resp;
}
int getDist() {
long duration, distance;
digitalWrite( trig, LOW ); // init pin low
delayMicroseconds( 2 ); // probably not exactly necessary
digitalWrite( trig, HIGH ); // start trigger
delayMicroseconds( 10 ); // hold a little bit
digitalWrite( trig, LOW ); // end trigger
// get time until pulse in?
duration = pulseIn( echo, HIGH );
Serial.print( "duration (raw): " );
Serial.println( duration );
Serial.print( "half TOF: " );
Serial.println( duration / 2 );
// calculate distance from time of flight (TOF)
// duration / 2 -> TOF is there and back, get there
// * 0.034 -> cm / us
distance = ( duration / 2 ) * Vs;
Serial.print( "dist: " );
Serial.println( distance );
Serial.print( "string: " );
Serial.println( String( distance ) );
return distance;
}
String getDepth() {
// if units are inches, convert, else scale by 1
int deepness = inch ? depth * CMtoIN : depth;
// convert to string
String response = String( deepness );
// append correct units
response += inch ? " [in]" : " [cm]";
return response;
}
// ===================================================================
String prse( String command ) {
int amnt = 0; // keep track of amount of spaces
int len = command.length(); // capture length
// quick variables to keep track of command
bool ask = false;
bool tell = false;
bool list = false;
// init failure string
String response = "error: command unrecognized";
// iterate over each letter of incoming message
for( int i = 0; i < command.length(); i++ ) {
// when letter is space...
if( command[i] == ' ' ) {
amnt++; // increment count for number of spaces
// on the first space
if( amnt == 1 ) {
// print first command
Serial.print( "ask/tell: " );
Serial.println( command.substring( 0, i ) );
// snatch bools for various commands
ask = command.substring( 0, i ) == "ask";
tell = command.substring( 0, i ) == "tell";
list = command.substring( 0, i ) == "list";
// print bools
Serial.println( ask );
Serial.println( tell );
Serial.println( list );
// seemed hacky but it fuckin works
// if command was list, get list and return it
if( list ) {
Serial.println( "LIST" );
response = lst();
Serial.println( response );
}
// on the second space
} else if( amnt == 2 ) {
// capture everything after third word
String argument = command.substring( i + 1, len );
// if user is asking ( should make argument one word )
if( ask ) {
// get distance before doing anything
// not always necessary, but isn't resource intensive
int distance = getDist();
if( argument == "dist" ) { // user wants dist
// if units are inches, convert, else scale by 1
distance *= inch ? CMtoIN : 1;
response = String( distance ); // set return var
// append correct units
response += inch ? " [in]" : " [cm]";
} else if( argument == "fill" ) { // user wants fill
// if distance is smaller than depth, distance / depth will
// be 0, because int. scale up before taking fraction to find
// percent empty space
// sub from 100 to find the actual fill percent
distance = 100 - ( distance * 100 ) / depth;
response = String( distance ) + "%"; // append unit
} else if( argument == "depth" ) { // user wants depth
response = getDepth();
} else if( argument == "unit" ) { // user wants units
response = inch ? "[in]" : "[cm]";
} else { // doesn't want status or color, arg unknown
response = "field unrecognized";
}
// node doesn't work with tell
} else if( tell ) {
response = "/tell/ is under construction";
// capture number of letters in argument
int arglen = argument.length();
// bool to keep track of if a space was seen
// if there was no space seen, arg is very incorrect
bool space = false;
// iterate over characters in argument
for( int j = 0; j < arglen; j++ ) {
// if letter is a space
if( argument[j] == ' ' ) {
// mark space as true
space = true;
// grab everything before the space, subcommand
String subcom = argument.substring( 0, j );
// grab everything after the space, subargument
String subarg = argument.substring( j + 1, arglen );
// user is redefining food depth
if( subcom == "depth" ) {
if( subarg.toInt() != 0 ) {
depth = subarg.toInt();
depth /= inch ? CMtoIN : 1;
response = getDepth();
} else {
response = "depth argument must be an int";
}
// user is redefining units
} else if( subcom == "unit" ) {
// check if subarg is valid
if( subarg == "cm" ) {
inch = false; // false for cm
response = getDepth();
} else if( subarg == "in" || subarg == "inch"
|| subarg == "inches" ){
inch = true; // true for inches
response = getDepth();
} else { // if input is invalid, don't change
// say so, don't change anything
response = "argument unrecognized";
}
} else { // command is neither depth nor unit
// say so, don't change anything
response = "command unrecognized";
}
// break out once space is found
// no need to keep iterating once space is found
break;
}
}
// if space was never found, argument was very wrong
if( !space ) {
// say so, don't change anything
response = "/" + argument + "/ invalid";
}
} else { // if user is neither asking nor telling
// also not list
// say so, don't change anything
response = "command unrecognized";
}
}
}
}
Serial.print( "resp IN:" );
Serial.println( response );
return response; // return response
}
// ===================================================================
// Loop
// ===================================================================
void loop() {
// returns client one exists
WiFiClient client = server.available();
if( client ) { // if server.available() finds client
if( client.connected() ) { // client has connected
Serial.println( "Client Connected" );
}
String response = ""; // init response variable
// for as long as client stays connected
while( client.connected() ) {
// for as long as there are bytes to be read, read em
while( client.available() > 0 ) {
// read data from the connected client
String command = client.readStringUntil( '\n' );
//digitalWrite( pinBI, statusBI = !statusBI );
Serial.println( command ); // print to serial monitor
int count = 0;
int previous = 0; // keep track of spaces
// iterate over string
for( int i = 0; i <= command.length(); i++ ) {
// if char is space or end of string
if( command[i] == ' ' || command[i] == '\0') {
// print each word in command (except last one)
Serial.println( command.substring( previous, i ) );
if( count == 1 ) { // check second word for name
// if command was meant for me...
if( command.substring( previous, i ) == NAME ) {
// parse command
response = prse( command );
Serial.print( "resp OUT:" );
Serial.println( response );
//response = "not just yet";
} else { // tell user message doesn't belong here
// this shouldn't be possible, but
response = "node mismatch";
}
// once response is set, leave loop and send
break;
}
// set previous to AFTER space, to capture ONLY the word
previous = i + 1;
count++; // increment count of spaces
}
}
// print rest of message to monitor
Serial.println( command.substring( previous, command.length() ) );
client.println( response ); // send entire command to client
}
}
client.stop(); // stop client when done reading
Serial.println( "Client disconnected" ); // print
}
}
//=======================================================================
+131 -23
View File
@@ -1,11 +1,12 @@
// =============================================================================
// Auth: Alex Celani
// File: sentinel.go
// Revn: 06-27-2022 1.3
// Revn: 07-14-2022 3.0
// Func: Receive message, determine recipient, forward, get response,
// forward back
//
// TODO:
// TODO: add time flag
// use tail command line args to send single message and quit
// =============================================================================
// CHANGE LOG
// -----------------------------------------------------------------------------
@@ -19,6 +20,13 @@
// 06-26-2022: refined a new route() function to check for destination
// validity
// 06-27-2022: added flag package
// 07-05-2022: added support for kill messages
//*07-06-2022: added support for mulitplexing multiple endpoints
// so long as their name and ip is in /nodes/ file
// 07-11-2022: added solo mode and flag
// changed how list works
// 07-12-2022: comments
//*07-14-2022: changed input/output prompt to arrow
//
// =============================================================================
@@ -31,9 +39,20 @@ import (
"fmt" // Println, Fprintf
"strings" // ToLower, ToUpper
"flag" // Parse, String, Bool
"io/ioutil" // ReadFile
"bufio" // input()
)
// quick function to take user input
func input( ps1 string ) string {
fmt.Print( ps1 ) // print prompt
scanner := bufio.NewScanner( os.Stdin ) // link to stdin
scanner.Scan() // pull data
return strings.ToLower( scanner.Text() ) // return lower text
}
// quick error checking
func check( err error ){
// if user checks an error, it better be nil
@@ -46,16 +65,17 @@ func check( err error ){
// read the given message and route to the right endpoint
func route( recv []string ) bool {
return recv[1] == "led" // only option right now is led
func route( recv []string ) string {
ip := nodes[recv[1]] // get correlating ip to given node name
return ip
}
// function to send message to third party and get response
func send( toSend string ) string {
func send( toSend, ip string ) string {
// "resolve" ip & host according to TCP rules
tcpAddr, err := net.ResolveTCPAddr( "tcp", *ip )
tcpAddr, err := net.ResolveTCPAddr( "tcp", ip )
check( err ) // check error
// "dial" ( establish connection ) to destination ip & port
@@ -81,6 +101,64 @@ func send( toSend string ) string {
}
// parse input, send to node, return response
func forward( recv string ) string {
// break commands into words
keywords := strings.Split( recv, " " )
length := len( keywords ) // store length for later use
// declare return variable
var resp string
if length == 1 { // if length of input is only 1
if keywords[0] == "list" { // command better be /list/
var i int = 0 // var to keep track of iterations
// iterate over keys in map to get list of node names
for name, _ := range nodes {
if i != 0 {
// do not prepend spaces to first node
resp = resp + " "
}
resp = resp + name + "\n" // append node name
i++ // keep track of iterations
}
resp = resp[:len( resp ) - 1] // remove last newline
} else { // command is not /list/, ergo
// not supported
resp = "command unrecognized"
}
} else if length > 1 { // command is more than one word
// user wants a comprehensive list of nodes and fields
if keywords[0] == "list" && keywords[1] == "-v" {
// iterate over all nodes
for name, addr := range nodes {
// add name, ip, newline, and justifying spaces
resp = resp + name + " --> " + addr + "\n "
// send command /list/ to node
resp = resp + send( "list " + name, addr )
// add newline and justifying spaces
resp = resp + "\n "
}
// remove trailing newline and spaces
resp = resp[:len( resp ) - 4]
} else { // user wants a different command
ip := route( keywords ) // find appropriate ip
if ip == "" { // ip not found, node DNE
resp = "node unrecognized" // print error
} else { // node does exist
// send message to node, get response
resp = send( recv, ip )
}
}
} else { // length of command was 0
resp = "command unrecognized"
}
return resp // return response
}
// function to handle incoming connections
func handleClient( conn net.Conn ) {
defer conn.Close() // barring any error, still close connection
@@ -103,46 +181,76 @@ func handleClient( conn net.Conn ) {
fmt.Println( "recv: ", recv )
}
keywords := strings.Split( recv, " " )
// parse the input in some way
var resp string // declare response variable
if route( keywords ) { // is destination valid?
resp = send( recv ) // send message and get response
// if user wants a list of endpoints
} else if keywords[1] == "list" {
resp = "led" // return list of endpoints
}
if *verbose {
fmt.Println( "node: ", resp ) // print response
}
// parse input, send out to node, get response
resp := forward( recv )
// write that response back to original client
_, err = conn.Write( []byte( resp ) )
if err != nil { // erroring on write will simply leave the
return // function so it can start again later
}
if resp == "kill" { // if received kill message
os.Exit( 1 ) // kill process
}
}
}
// function to read in initialization data for nodes
func initNodes() {
// open and read config file
file, err := ioutil.ReadFile( "nodes" )
check( err ) // error checking on read
// cast file to string, split string over newlines
node := strings.Split( string( file ), "\n" )
for i, kvp := range node { // iterate over lines
// XXX for whatever reason, the EOF is seen as a newline?
// so this only works for all but the "last line"
if i != len( node ) - 1 {
// kvp is delimited by a bar, split over bar
// key is the name of the endpoint
// ip is the ip of the same endpoint
nameip := strings.Split( kvp, "|" )
// insert kvp into map
nodes[nameip[0]] = nameip[1]
}
}
}
var ( // declare global variables for flag
ip *string
self *string
verbose *bool
solo *bool
)
// declare global variable to contain names and ips of endpoints
var nodes = make( map[string]string )
func main() {
// get flags for verbose, self ip and destination ip
verbose = flag.Bool( "v", false, "flag to print extra info" )
ip = flag.String( "ip", "192.168.1.169:1202", "end node ip and port" )
self = flag.String( "self", ":1201", "port of self" )
solo = flag.Bool( "s", false, "run sentinel in solo mode" )
flag.Parse() // parse
// "resolve" ip & host according to TCP rules
// read /nodes/ file and populate nodes map
initNodes()
if *solo { // if user requested stand-alone action
for { // iterate forever ( until user quits )
in := input( "-> " ) // print prompt, take input
resp := forward( in ) // forward directly to node
fmt.Println( "<-", resp ) // print response
}
}
tcpAddr, err := net.ResolveTCPAddr( "tcp", *self )
check( err ) // check error