Skip to content

Commit

Permalink
chore: update instance generator
Browse files Browse the repository at this point in the history
  • Loading branch information
alberto-abarzua committed Nov 17, 2023
1 parent a653b2c commit 3c58b3f
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 139 deletions.
Binary file modified firmware/app
Binary file not shown.
1 change: 1 addition & 0 deletions firmware/components/controller/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ void Controller::message_handler_config(Message *message) {

EndStop *end_stop = new DummyEndStop(
0, new_movement_driver->get_current_angle_ptr());

this->joints[joint_idx]->set_movement_driver(new_movement_driver);
this->joints[joint_idx]->register_end_stop(end_stop);
new_movement_driver->hardware_setup();
Expand Down
34 changes: 1 addition & 33 deletions firmware/components/movement/movement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,39 +107,7 @@ uint32_t MovementDriver::steps_to_take(uint64_t current_time) {
this->angle_to_steps(this->current_angle));
return std::min(steps, steps_to_target);
}
// bool MovementDriver::step() {
// uint64_t current_time = get_current_time_microseconds();
// uint32_t steps_to_take = this->steps_to_take(current_time);

// if (steps_to_take > 0) {

// int8_t step_dir = this->target_angle > this->current_angle ? 1 : -1;

// for (uint16_t i = 0; i < steps_to_take; i++) {
// this->current_steps += step_dir;

// this->hardware_step(step_dir > 0 ? 1 : 0);

// if (!this->homed && this->end_stop != nullptr) {
// this->current_angle =
// this->steps_to_angle(this->current_steps); if
// (this->end_stop->hardware_read_state()) {
// this->set_home();
// this->last_step_time = get_current_time_microseconds();
// return false;
// }
// run_delay_microseconds(10);
// } else {
// run_delay_microseconds(30);
// }
// }

// this->last_step_time = get_current_time_microseconds();
// this->current_angle = this->steps_to_angle(this->current_steps);
// return true;
// }
// return false;
// }

bool MovementDriver::step() {
uint64_t current_time = get_current_time_microseconds();
uint32_t steps_to_take = this->steps_to_take(current_time);
Expand Down
1 change: 1 addition & 0 deletions firmware/components/movement/servo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ Servo::Servo(int8_t pin) {
this->set_speed(0.5);
this->set_steps_per_revolution(720);
}

Servo::~Servo() {
if (this->end_stop != nullptr) {
delete this->end_stop;
Expand Down
2 changes: 1 addition & 1 deletion firmware/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
#define WIFI_SSID "PALVI"
#define WIFI_PASSWORD "Palvi.1400"
#define CONTROLLER_SERVER_HOST "backend"
#define CONTROLLER_SERVER_PORT 8500
#define CONTROLLER_SERVER_PORT 8300

#endif // CONFIG_H
5 changes: 5 additions & 0 deletions instanciator/backend/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[flake8]
max-line-length = 110
ignore = E203, W503, ANN101,ANN401,ANN102
exclude = .git,__pycache__,migrations,venv,build,dist,.venv
select = B,C,E,F,W,T4,B9,E901,E999,F821,F822,F823,T201,T203,T204,ANN
Empty file.
172 changes: 107 additions & 65 deletions instanciator/backend/src/instance_generator.py
Original file line number Diff line number Diff line change
@@ -1,147 +1,189 @@
import json
import os
import socket
import threading
import subprocess
import os
import threading
import time
import uuid
from pathlib import Path
from typing import Any, Dict, Optional

import redis
import json
import random

PARENT_FILE_PATH = Path(__file__).parent


DOCKER_COMPOSE_FILE_PATH = PARENT_FILE_PATH / "firmware-controller-compose.yaml"
DOCKER_COMPOSE_FILE_PATH = (
PARENT_FILE_PATH / "config" / "firmware-controller-compose.yaml"
)


class InstanceGenerator:
def __init__(self):
def __init__(self) -> None:
self.docker_compose_path = str(DOCKER_COMPOSE_FILE_PATH)
self.redis_client = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True)
self.redis_client = redis.Redis(
host="localhost", port=6379, db=0, decode_responses=True
)
self.start_instance_checker()

def get_instances(self):
def isntance_checker_target_fun(self) -> None:
while True:
instances = self.instances
free_instances = 0

for instance in instances.values():
instance_uuid = instance["uuid"]

if time.time() - instance["time_started"] > 30 * 60:
self.destroy(instance_uuid)
continue

if instance["free"]:
free_instances += 1

if free_instances <= 3:
new_uuid = str(uuid.uuid4())
new_instance = self.create_instance(new_uuid)
new_instance["free"] = True
instances[new_uuid] = new_instance

self.instances = instances

time.sleep(5)

def start_instance_checker(self) -> None:
thread = threading.Thread(target=self.isntance_checker_target_fun)
thread.start()

def get_instances(self) -> Dict[str, Any]:
return self.instances

@property
def instances(self):
json_str = self.redis_client.get('instances')
def instances(self) -> Dict[str, Any]:
json_str = self.redis_client.get("instances")
if json_str is None:
return {}
else:
return json.loads(str(json_str))

@instances.setter
def instances(self, value):
self.redis_client.set('instances', json.dumps(value))
def instances(self, value: Dict[str, Any]) -> None:
self.redis_client.set("instances", json.dumps(value))

def get_instance(self, uuid_str):
def get_instance(self, uuid_str: str) -> Optional[Dict[str, Any]]:
if uuid_str in self.instances:
return self.instances[uuid_str]
else:
return None
return None

def isntance_checker_target_fun(self):
while True:
for uuid_str in self.instances:
instance = self.instances[uuid_str]
if time.time() - instance['time_started'] > 30 * 60:
self.destroy(uuid_str)
time.sleep(10)

def start_instance_checker(self):
thread = threading.Thread(target=self.isntance_checker_target_fun)
thread.start()

def get_free_port(self):
def get_free_port(self) -> int:
while True:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(('', 0)) # Bind to port 0 to let the OS choose an available port
s.bind(("", 0)) # Bind to port 0 to let the OS choose an available port
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
port = s.getsockname()[1]
return port

def get_project_name(self, uuid_str):
def get_project_name(self, uuid_str: str) -> str:
return f"rtwin_instance_{uuid_str}"

def get_or_create(self, uuid_str):
def get_uuid(self) -> str:
instances = self.instances
for instance in instances.values():
if instance["free"]:
instance["free"] = False
return instance["uuid"]

new_uuid = str(uuid.uuid4())
new_instance = self.create_instance(new_uuid)
new_instance["free"] = False
instances[new_uuid] = new_instance
self.instances = instances
return new_uuid

if uuid_str in instances:
return instances[uuid_str]

# Generate unique ports
def create_instance(self, uuid_str: str) -> Dict[str, Any]:
backend_http_port = self.get_free_port()
controller_websocket_port = self.get_free_port()
controller_server_port = self.get_free_port()

# Set environment variables for docker ","compose
env_vars = {
"BACKEND_HTTP_PORT": str(backend_http_port),
"CONTROLLER_WEBSOCKET_PORT": str(controller_websocket_port),
"CONTROLLER_SERVER_PORT": str(controller_server_port),
"ESP_CONTROLLER_SERVER_PORT": str(controller_server_port),
}

# Define the project name based on UUID
project_name = self.get_project_name(uuid_str)

# Launch docker ","compose
# result = subprocess.run(
# ["docker ","compose", "-f", self.docker_compose_path, "-p", project_name, "up", "-d"],
# env={**os.environ, **env_vars})

command = ["docker", "compose", "-f", self.docker_compose_path, "-p", project_name, "up", "-d"]
command = [
"docker",
"compose",
"-f",
self.docker_compose_path,
"-p",
project_name,
"up",
"-d",
]

result = subprocess.check_call(command, env={**os.environ, **env_vars})
if result != 0:
raise Exception("docker compose failed")

# Store instance data
instances[uuid_str] = {
return {
"ports": {
"backend_http_port": backend_http_port,
"controller_websocket_port": controller_websocket_port,
"controller_server_port": controller_server_port
"controller_server_port": controller_server_port,
},
'time_started': time.time(),

"time_started": time.time(),
"free": True,
"uuid": uuid_str,
}

self.instances = instances

return instances[uuid_str]

def destroy(self, uuid_str):
def destroy(self, uuid_str: str) -> None:
if uuid_str in self.instances:
project_name = self.get_project_name(uuid_str)
isntances = self.instances
instance = isntances[uuid_str]
instances = self.instances
instance = instances[uuid_str]

# get env variables
env_vars = {
"BACKEND_HTTP_PORT": str(instance['ports']['backend_http_port']),
"CONTROLLER_WEBSOCKET_PORT": str(instance['ports']['controller_websocket_port']),
"CONTROLLER_SERVER_PORT": str(instance['ports']['controller_server_port']),
"ESP_CONTROLLER_SERVER_PORT": str(instance['ports']['controller_server_port']),
"BACKEND_HTTP_PORT": str(instance["ports"]["backend_http_port"]),
"CONTROLLER_WEBSOCKET_PORT": str(
instance["ports"]["controller_websocket_port"]
),
"CONTROLLER_SERVER_PORT": str(
instance["ports"]["controller_server_port"]
),
"ESP_CONTROLLER_SERVER_PORT": str(
instance["ports"]["controller_server_port"]
),
}
command = ["docker", "compose", "-f", self.docker_compose_path,
"-p", project_name, "down", "--remove-orphans"]
command = [
"docker",
"compose",
"-f",
self.docker_compose_path,
"-p",
project_name,
"down",
"--remove-orphans",
]
result = subprocess.check_call(command, env={**os.environ, **env_vars})
if result != 0:
raise Exception("docker ", "compose failed")
instances = self.instances
del instances[uuid_str]
self.instances = instances

def destroy_all(self):
def destroy_all(self) -> None:
instances = self.instances
for uuid_str in instances:
self.destroy(uuid_str)

def get_backend_port(self, uuid_str):
def get_backend_port(self, uuid_str: str) -> Optional[int]:
instances = self.instances
port = None
if uuid_str in instances:
port = instances[uuid_str]['ports']['backend_http_port']
port = instances[uuid_str]["ports"]["backend_http_port"]

return port
Loading

0 comments on commit 3c58b3f

Please sign in to comment.