Commit 653ca84d authored by Philip Levis's avatar Philip Levis
Browse files

Refactoring and reorganizing simulat or code.

parent ab0736e6
# FLIGHT: Packard Interactive Light Sculpture Project
# FLIGHT Simulator
The FLIGHT simulator is a Linux program that appears to be the FLIGHT
installation and responds to scripting commands. It is structured as
follows:
o [fsimulator](fsimulator): the main executable for running the simulator.
o [graphics](graphics): the graphics library for displaying the simulated
state of FLIGHT.
o [client](client): a simple test client for interacting with the simulator.
Splits API for simulator into two parts; the interpreter/data_processing side
(in interpreter/) and the client side/main simulator process (server.c)
Normally, each Fractal Flyer in FLIGHT is plugged into a computer with
USB and appears in two ways, as a serial port presenting a Python
read-eval-print-loop and as a removeable USB drive that you can store
Python code on. When the FLIGHT simulator boots, it creates 76 entries
in the local file system that act like serial ports and 76 directories
that you can copy Python code into. It starts up 76 Python
interpreters which receive Python scripting commands from the
simulated serial ports. These 76 interpreters send commands to a
server process, which simulates the lights and motion of the Fractal
Flyers and displays them graphically.
This directory stores source code for a graphical FLIGHT simulator. The simulator
appears exactly as the art installation does, like 76 CircuitPython instances. You
can program and visualize how they look using a high-quality 3D visualization.
![The high level flow of the simulator is summarized in this diagram.](../SimulatorFlow.png)
The high level flow of the simulator is summarized in this diagram:
(https://code.stanford.edu/plevis/ee185/-/blob/master/software/simulator/SimulatorFlow.png)
Currently, the simulation component of the simulator is complete but
the graphics are unfinished.
#PYTHON API FORMAT
## Python API
For a Python API, I’d suggest these three functions:
Fractal Flyers provide 3 Python functions to control their lights and wings:
``` Python
wing_leds(LEFT|RIGHT, (r, g, b), (r, g, b,), (r, g, b,))
body_leds((r, g, b)
wing_angle(LEFT|RIGHT, angle)
```
Where r g and b are 0-255 and angle is -90.0 - 90.0.
The assumption is the LED changes are near-instantaneous. The angle change,
however, may take time. The FF is smart enough to know its current wing position
and velocity and just try to move to the most recent angle command. So, suppose
the left wing is at 0 and you call wing_angle(LEFT, -20),the left wing will
start to lower to -20. Suppose, when it is at -10, you call wing_angle(LEFT, -30).
It will keep on lowering until it reaches -30. If, when it is at -25, you call
wing_angle(LEFT, 10), it will slow and stop, then start moving to 10.
This will all occur on the firmware.
The assumption is the LED changes are near-instantaneous. The angle
change, however, may take time. The FF is smart enough to know its
current wing position and velocity and just try to move to the most
recent angle command. So, suppose the left wing is at 0 and you call
wing_angle(LEFT, -20),the left wing will start to lower to
-20. Suppose, when it is at -10, you call wing_angle(LEFT, -30). It
will keep on lowering until it reaches -30. If, when it is at -25, you
call wing_angle(LEFT, 10), it will slow and stop, then start moving
to 10. This will all occur on the firmware.
Splits API for simulator into two parts; the interpreter/data_processing side
(in interpreter/) and the client side/main simulator process (server.c)
## Interpreter ##
Contains
- Data_processor.cc: Main process for reading from engine output, spawning
76 processes, and transferring data into the main simulator process.
# Flight Simulator
- simLib.c: The symbolic library containing C bindings for the engine's
python API.
This directory contains the main source code for the Flight simulator. To run
it, follow these steps:
1. Install the Python library for simulating a Fractal Flyer in a Python interpreter: `python3 setup.py install`. You may need to run this with `sudo`.
1. Compile the simulator: `make`.
1. Run the simulator: `./fsimulator`.
The simulator exposes a simple console interface.
- api.py: A local version of the python API for use in binding incoming
## Files
-fsimulator.cc: Main process for reading from engine output, spawning
76 processes, and transferring data into the main simulator process.
- simLib.c: The symbolic library containing C bindings for the engine's
python API.
- api.py: A local version of the python API for use in binding incoming
python commands to their simLib versions.
- server.cc: networking code that receives commands from the 76 Python interpreters.
To compile data_processor: g++ data_processor.cc -I/usr/include/python3.6 -lpython3.6m -o data_processor
To install simLib on your local machine: python3 setup.py install
To restart interpreter for fractal flyer type the idx of the FF and hit enter while running ./data_processor
To shutdown entire interpreter type in -1 and hit enter
### fsimulator.cc
Data_processor.cc:
This file contains the main code of the simulator which creates all 76 flyer emulations, runs the interpreters, and runs the server side implementation. From here the process is able to send signals to shutdown or restart any interpreter and manage the entire simulation. It creates the server followed by the flyer emulations. Each emulation creates a FIFO. All the FIFO are stored at /tmp/fractal_flyers/flyeri where i is the index of the fractal flyer. If directory /tmp/fractal_flyers does not exist, it is created. Both of these file structures are destroyed when the process is closed or crashes. After creating the FIFOS the process then runs on an interpreter on a new process which reads from the FIFO and runs the commands. Before this is done, a set of initalization python commands are run which connect to the server and communicate information about the fractal flyer. The commands later sent on that flyer therefore have context that they are sent by a specific fractal flyer. If the fractal flyer is in a while loop, the interpreter is closed and reopened. The entire python environment and objects are destroyed and the interpreter is reopened, reconnects, and is then able again to read commands from the FIFO. While the interpreters are running, the main process is waiting on user input. If the user types in -1 or ctrl-C a signal is sent to kill all of the processes, close the file structures, and cleanly close the program. If the user types in any positive number, that flyer index is restarted. This is intended to resolve any issues with inifinite while loops which can stall an interpreter indefinetly.
......
......@@ -11,7 +11,7 @@
#include "flight_server.h"
#define PORT 4040
#define PORT 0xf100
#define NUM_FLYERS 76
#define BODYLED 1
#define WINGLED 2
......
......@@ -185,16 +185,21 @@ int main(int argc, char *argv[])
if (command == 'k') {
kill_all_processes(-1);
} else if(command == 'r') {
scanf(" %d",&flyer);
if (flyer >= num_flyers || flyer < 0) {
printf("Invalid FF id\n");
int count = scanf(" %d",&flyer);
if (count != 1) {
printf("usage: r ID\n");
continue;
} else if (flyer >= num_flyers || flyer < 0) {
printf("Invalid flyer id: %d\n", flyer);
continue;
}
kill(flyers[flyer].pid,SIGKILL);
flyers[flyer].pid = fork();
if (flyers[flyer].pid == 0) {
} else {
printf("Restarting flyer %d\n", flyer);
kill(flyers[flyer].pid,SIGKILL);
flyers[flyer].pid = fork();
if (flyers[flyer].pid == 0) {
spawn_process(flyer);
}
}
}
} else if(command == 'h') {
cout << "k \tkills all processes" << endl;
cout << "r id \trestart fractal flyer with id" << endl;
......
......@@ -5,7 +5,7 @@
#include <unistd.h>
#include <string.h>
#include <errno.h>
#define PORT 8080
#define PORT 0xf100
int main(int argc, char const *argv[])
{
......@@ -45,4 +45,4 @@ int main(int argc, char const *argv[])
printf("Recieved message Len: %d\n",valread);
printf("Message Recieved: %s\n",message_recieved);
return 0;
}
\ No newline at end of file
}
// Server side C/C++ program from geeksforgeeks.org
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <poll.h>
#include <stdbool.h>
#include <mutex>
#define PORT 4040
#define NUM_FLYERS 76
#define BODYLED 1
#define WINGLED 2
#define WINGANGLE 3
#define LEFT 1
#define RIGHT 0
struct rgb{
int red;
int green;
int blue;
};
struct wing{
struct rgb node1;
struct rgb node2;
struct rgb node3;
double angle;
};
struct flyerInfo{
int number;
struct rgb body;
struct wing leftWing;
struct wing rightWing;
};
struct flyerInfo flyerStates[NUM_FLYERS] = {0};
std::mutex flyerLock;
/* Runs main serverCode which listens to all
* the fractal flyers and creates a singular
* model of the state of the fractal flyers
*/
int serverCode(){
int server_fd, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
// Forcefully attaching socket to the port 8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
// Forcefully attaching socket to the port 8080
if (bind(server_fd, (struct sockaddr *)&address,
sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
//create 64 threads to listen to each FF
if (listen(server_fd, 3) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
//pollfd contains one extra fd for the server to listen to new connections
struct pollfd fds[NUM_FLYERS+1];
for(int i = 0; i < NUM_FLYERS; i++){
int tempfd;
if ((tempfd = accept(server_fd, (struct sockaddr *)&address,
(socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
int id;
char message_recieved[1024] = {0};
//reads in message to id the connected FF
valread = read(tempfd, message_recieved, 1024);
sscanf(message_recieved,"%d",&id);
fds[id].fd = tempfd;
fds[i].events = POLLIN;
}
fds[NUM_FLYERS].fd = server_fd;
fds[NUM_FLYERS].events = POLLIN;
int total = NUM_FLYERS;
flyerLock.lock();
while(total != 0){
bool change = false;
flyerLock.unlock();
int num = poll(fds,NUM_FLYERS+1,-1);
flyerLock.lock();
if(num == -1)
perror("poll");
else if(num == 0)
perror("timeout");
//loop over all FF + listen of connections
for(int i = 0; i < NUM_FLYERS+1; i++){
if (fds[i].revents & POLLIN) {
if(i == NUM_FLYERS){
int tempfd;
if ((tempfd = accept(server_fd, (struct sockaddr *)&address,
(socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
int id;
char message_recieved[1024] = {0};
//reads in message to id the connected FF
valread = read(tempfd, message_recieved, 1024);
sscanf(message_recieved,"%d",&id);
//temperary code where simulator can request the flyer state
if(id == -1){
for(int j = 0; j < NUM_FLYERS; j++){
char output[1024] = {0};
sprintf(output,"\nFlyer:%d:%d\nBody:(%d,%d,%d)\nLeftWing:(%d,%d,%d):"
"(%d,%d,%d):(%d,%d,%d):Angle:%lf\nRightWing:(%d,%d,%d):"
"(%d,%d,%d):(%d,%d,%d):Angle:%lf\n\n",i,flyerStates[j].number,
flyerStates[j].body.red,flyerStates[j].body.green,
flyerStates[j].body.blue,flyerStates[j].leftWing.node1.red,
flyerStates[j].leftWing.node1.green,flyerStates[j].leftWing.node1.blue,
flyerStates[j].leftWing.node2.red,flyerStates[j].leftWing.node2.green,
flyerStates[j].leftWing.node2.blue,flyerStates[j].leftWing.node3.red,
flyerStates[j].leftWing.node3.green,flyerStates[j].leftWing.node3.blue,
flyerStates[j].leftWing.angle,flyerStates[j].rightWing.node1.red,
flyerStates[j].rightWing.node1.green,flyerStates[j].rightWing.node1.blue,
flyerStates[j].rightWing.node2.red,flyerStates[j].rightWing.node2.green,
flyerStates[j].rightWing.node2.blue,flyerStates[j].rightWing.node3.red,
flyerStates[j].rightWing.node3.green,flyerStates[j].rightWing.node3.blue,
flyerStates[j].rightWing.angle);
send(tempfd , output , strlen(output) , 0 );
}
send(tempfd, "EOF",strlen("EOF"),0);
close(tempfd);
}else{
fds[id].fd = tempfd;
//replaces old FF connection with new
printf("Opened FF %d\n",id);
}
break;
}
char message_recieved[1024] = {0};
char *message_send = "Hello from server!!";
//close directory when you cant read anything
valread = read( fds[i].fd , message_recieved, 1024);
if(valread <= 0){
fds[i].fd = -1;
printf("Closed FF %d\n",i);
continue;
}
int temploc;
int tempCommand;
char tempRest[100];
sscanf(message_recieved,"%d:%d:%s",&temploc,&tempCommand,tempRest);
flyerStates[temploc].number = temploc;
change = true;
if(tempCommand == BODYLED){
struct rgb rgbTemp;
sscanf(tempRest,"%d,%d,%d",&rgbTemp.red,&rgbTemp.green,&rgbTemp.blue);
flyerStates[temploc].body = rgbTemp;
}
else if(tempCommand == WINGLED){
struct rgb rgbNode1Temp;
struct rgb rgbNode2Temp;
struct rgb rgbNode3Temp;
bool direction;
sscanf(tempRest,"%d:%d,%d,%d:%d,%d,%d:%d,%d,%d",
(int*)&direction,&rgbNode1Temp.red,&rgbNode1Temp.green,
&rgbNode1Temp.blue,&rgbNode2Temp.red,&rgbNode2Temp.green,
&rgbNode2Temp.blue,&rgbNode3Temp.red,&rgbNode3Temp.green,
&rgbNode3Temp.blue);
if (direction == LEFT){
flyerStates[temploc].leftWing.node1 = rgbNode1Temp;
flyerStates[temploc].leftWing.node2 = rgbNode2Temp;
flyerStates[temploc].leftWing.node3 = rgbNode3Temp;
}else{
flyerStates[temploc].rightWing.node1 = rgbNode1Temp;
flyerStates[temploc].rightWing.node2 = rgbNode2Temp;
flyerStates[temploc].rightWing.node3 = rgbNode3Temp;
}
}else if(tempCommand == WINGANGLE){
bool direction;
double angle;
sscanf(tempRest,"%d:%lf",(int*)&direction,&angle);
if(direction == LEFT){
flyerStates[temploc].leftWing.angle = angle;
}else{
flyerStates[temploc].rightWing.angle = angle;
}
}
//printf("Recieved message Len: %d\n",valread);
printf("Message Recieved: %s\n",message_recieved);
// send(fds[i].fd , message_send , strlen(message_send) , 0 );
// //printf("Sent message Len: %lu\n",strlen(message_send));
//printf("Message sent: %s\n",message_send);
}
}
if(change){
for(int i = 0; i < NUM_FLYERS; i++){
printf("\nFlyer:%d:%d\nBody:(%d,%d,%d)\nLeftWing:(%d,%d,%d):"
"(%d,%d,%d):(%d,%d,%d):Angle:%lf\nRightWing:(%d,%d,%d):"
"(%d,%d,%d):(%d,%d,%d):Angle:%lf\n\n",i,flyerStates[i].number,
flyerStates[i].body.red,flyerStates[i].body.green,
flyerStates[i].body.blue,flyerStates[i].leftWing.node1.red,
flyerStates[i].leftWing.node1.green,flyerStates[i].leftWing.node1.blue,
flyerStates[i].leftWing.node2.red,flyerStates[i].leftWing.node2.green,
flyerStates[i].leftWing.node2.blue,flyerStates[i].leftWing.node3.red,
flyerStates[i].leftWing.node3.green,flyerStates[i].leftWing.node3.blue,
flyerStates[i].leftWing.angle,flyerStates[i].rightWing.node1.red,
flyerStates[i].rightWing.node1.green,flyerStates[i].rightWing.node1.blue,
flyerStates[i].rightWing.node2.red,flyerStates[i].rightWing.node2.green,
flyerStates[i].rightWing.node2.blue,flyerStates[i].rightWing.node3.red,
flyerStates[i].rightWing.node3.green,flyerStates[i].rightWing.node3.blue,
flyerStates[i].rightWing.angle);
}
}
}
return 0;
}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment