diff --git a/common/autoware_control_center/CMakeLists.txt b/common/autoware_control_center/CMakeLists.txt
new file mode 100644
index 00000000..6a5f4785
--- /dev/null
+++ b/common/autoware_control_center/CMakeLists.txt
@@ -0,0 +1,37 @@
+cmake_minimum_required(VERSION 3.8)
+project(autoware_control_center)
+
+find_package(autoware_cmake REQUIRED)
+autoware_package()
+
+ament_auto_add_library(${PROJECT_NAME} SHARED
+ src/control_center_node.cpp
+ src/node_registry.cpp
+ src/include/control_center_node.hpp
+ src/include/node_registry.hpp
+)
+
+#rclcpp_components_register_node(${PROJECT_NAME}
+# PLUGIN "autoware::control_center::ControlCenter"
+# EXECUTABLE ${PROJECT_NAME}_node
+#)
+
+ament_auto_add_executable(${PROJECT_NAME}_node
+ src/control_center_main.cpp
+)
+
+if(BUILD_TESTING)
+ file(GLOB_RECURSE TEST_SOURCES test/*.cpp)
+ ament_add_ros_isolated_gtest(test_autoware_control_center ${TEST_SOURCES})
+ target_include_directories(test_autoware_control_center PRIVATE src/include)
+ target_link_libraries(test_autoware_control_center ${PROJECT_NAME})
+ ament_target_dependencies(test_autoware_control_center
+ rclcpp
+ rclcpp_lifecycle
+ autoware_utils
+ autoware_control_center_msgs)
+endif()
+
+ament_auto_package(INSTALL_TO_SHARE
+ launch
+ config)
diff --git a/common/autoware_control_center/README.md b/common/autoware_control_center/README.md
new file mode 100644
index 00000000..bd59db7d
--- /dev/null
+++ b/common/autoware_control_center/README.md
@@ -0,0 +1,104 @@
+# Autoware Control Center
+
+## Abbreviations
+
+- **ACC:** Autoware Control Center
+- **AN:** Autoware Node
+
+## Overview
+
+The Autoware Control Center (ACC) is a core package within the Autoware system, designed to manage and monitor Autoware
+nodes (ANs).
+
+It provides services for the registration, de-registration, and error handling of ANs, as well as publishing reports on
+their status.
+
+ACC maintains an internal registry to keep track of registered ANs.
+
+## Interfaces
+
+### Services
+
+#### Register
+
+Registers an Autoware node to the ACC.
+
+- **Topic:** `/autoware/control_center/srv/register`
+- **Type:** `autoware_control_center_msgs::srv::Register`
+
+#### Deregister
+
+De-registers an Autoware node from the ACC.
+
+- **Topic:** `/autoware/control_center/srv/deregister`
+- **Type:** `autoware_control_center_msgs::srv::Deregister`
+
+#### ReportState
+
+Reports the state of an Autoware node to the ACC.
+
+- **Topic:** `/autoware/control_center/srv/report_state`
+- **Type:** `autoware_control_center_msgs::srv::ReportState`
+
+### Subscribers
+
+#### Heartbeat (**Source:** Autoware Node)
+
+Subscribes to the heartbeat topic of an Autoware node after its registration.
+ACC controls the liveness of Autoware nodes by monitoring this topic.
+
+- **Topic:** `/autoware_node_name/heartbeat`
+- **Type:** `autoware_control_center_msgs::msg::Heartbeat`
+- **Source:** An Autoware Node
+
+### Publishers
+
+#### NodeReports
+
+Publishes reports on the current status of registered Autoware nodes.
+
+- **Topic:** `/autoware/control_center/node_reports`
+- **Type:** `autoware_control_center_msgs::msg::NodeReport`
+
+## Parameters
+
+| Name | Type | Default Value | Description |
+| --------------------- | -------- | ------------- | ----------------------------------------------------------------------------------- |
+| `lease_duration_ms` | `double` | `220.0` | If not received another heartbeat within this duration, AN will be considered dead. |
+| `report_publish_rate` | `double` | `1.0` | Publish NodeReports rate (hz) |
+
+## Singleton Constraint
+
+To ensure that only one instance of the ACC is running at any given time, two checks are performed in the main function:
+
+### 1. Lockfile Check
+
+This lockfile mechanism is fast and reliable for single-machine scenarios.
+It prevents multiple instances of ACC from running concurrently on the same machine.
+
+**Path:** `/tmp/autoware_control_center_node.lock`
+
+### 2. Network-Wide Node Name Check
+
+This involves gathering all node names and comparing them with the ACC node name (`/autoware/control_center`).
+While this method is slower and less reliable than the lockfile check,
+it is necessary for scenarios where the ACC is run across a network of machines.
+This ensures that no other instance of ACC is running on any other machine within the network.
+
+## Usage
+
+`ros2 launch autoware_control_center control_center.launch.xml`
+
+## Workflow
+
+When an Autoware Node starts, it registers itself with the ACC.
+
+The ACC then subscribes to the heartbeat topic of the Autoware node to monitor its liveness.
+
+If the ACC does not receive a heartbeat from the Autoware node within the specified lease duration,
+it considers the node to be dead.
+
+## Credits
+
+- Heartbeat functionality is based on ROS 2 [software_watchdogs](https://github.com/ros-safety/software_watchdogs)
+ package.
diff --git a/common/autoware_control_center/config/control_center.param.yaml b/common/autoware_control_center/config/control_center.param.yaml
new file mode 100644
index 00000000..bbfd1f69
--- /dev/null
+++ b/common/autoware_control_center/config/control_center.param.yaml
@@ -0,0 +1,4 @@
+/**:
+ ros__parameters:
+ lease_duration_ms: 220.0
+ report_publish_rate: 1.0
diff --git a/common/autoware_control_center/launch/control_center.launch.xml b/common/autoware_control_center/launch/control_center.launch.xml
new file mode 100755
index 00000000..df115504
--- /dev/null
+++ b/common/autoware_control_center/launch/control_center.launch.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/common/autoware_control_center/package.xml b/common/autoware_control_center/package.xml
new file mode 100644
index 00000000..99ad6219
--- /dev/null
+++ b/common/autoware_control_center/package.xml
@@ -0,0 +1,32 @@
+
+
+
+ autoware_control_center
+ 0.0.0
+
+ Autoware Control Center (ACC) is an Autoware.Core package designed to manage
+ and monitor Autoware nodes within a system.
+
+ M. Fatih Cırıt
+ Apache-2.0
+
+ ament_cmake_auto
+ autoware_cmake
+
+ autoware_control_center_msgs
+ autoware_utils
+ lifecycle_msgs
+ rclcpp
+ rclcpp_components
+ rclcpp_lifecycle
+
+ ament_cmake_gtest
+ ament_cmake_ros
+ ament_lint_auto
+ autoware_lint_common
+ autoware_utils
+
+
+ ament_cmake
+
+
diff --git a/common/autoware_control_center/src/control_center_main.cpp b/common/autoware_control_center/src/control_center_main.cpp
new file mode 100644
index 00000000..65b99dce
--- /dev/null
+++ b/common/autoware_control_center/src/control_center_main.cpp
@@ -0,0 +1,93 @@
+// Copyright 2024 The Autoware Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "include/control_center_node.hpp"
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+const char * LOCK_FILE = "/tmp/autoware_control_center_node.lock";
+
+bool lock_file()
+{
+ int fd = open(LOCK_FILE, O_CREAT | O_RDWR, 0666);
+ if (fd == -1) {
+ RCLCPP_FATAL(rclcpp::get_logger(""), "Failed to open lock file");
+ return false;
+ }
+
+ if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
+ if (errno == EWOULDBLOCK) {
+ RCLCPP_FATAL(rclcpp::get_logger(""), "Another instance is already running");
+ } else {
+ RCLCPP_FATAL(rclcpp::get_logger(""), "Failed to lock file");
+ }
+ close(fd);
+ return false;
+ }
+
+ // Keep the file descriptor open to maintain the lock
+ return true;
+}
+
+void unlock_file(int fd)
+{
+ if (fd != -1) {
+ flock(fd, LOCK_UN);
+ close(fd);
+ }
+}
+
+int main(int argc, char * argv[])
+{
+ rclcpp::init(argc, argv);
+
+ // Lock file to prevent multiple instances on the same machine
+ int lock_fd = open(LOCK_FILE, O_CREAT | O_RDWR, 0666);
+ if (!lock_file()) {
+ return 1;
+ }
+
+ // Check if node already exists to prevent multiple instances across the network
+ // It's a bit slow but better than nothing
+ const auto node_name_with_namespace = std::string("/autoware/control_center");
+
+ auto node_already_exists = [](const std::string & node_name) {
+ auto temp_node = rclcpp::Node::make_shared("temp_node");
+ auto all_nodes = temp_node->get_node_names();
+ return std::find(all_nodes.begin(), all_nodes.end(), node_name) != all_nodes.end();
+ };
+
+ if (node_already_exists(node_name_with_namespace)) {
+ RCLCPP_FATAL(
+ rclcpp::get_logger(""), "Node %s already exists", node_name_with_namespace.c_str());
+ unlock_file(lock_fd);
+ throw std::runtime_error("Node already exists");
+ }
+
+ // Instantiate the control center node, hopefully the only instance
+ auto control_center =
+ std::make_shared(rclcpp::NodeOptions());
+ rclcpp::executors::MultiThreadedExecutor exec;
+ exec.add_node(control_center->get_node_base_interface());
+ exec.spin();
+ unlock_file(lock_fd);
+ rclcpp::shutdown();
+ return 0;
+}
diff --git a/common/autoware_control_center/src/control_center_node.cpp b/common/autoware_control_center/src/control_center_node.cpp
new file mode 100644
index 00000000..923268d8
--- /dev/null
+++ b/common/autoware_control_center/src/control_center_node.cpp
@@ -0,0 +1,186 @@
+// Copyright 2024 The Autoware Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "include/control_center_node.hpp"
+
+#include "include/node_registry.hpp"
+
+#include
+
+#include
+#include
+
+#include
+
+namespace autoware::control_center
+{
+ControlCenter::ControlCenter(const rclcpp::NodeOptions & options)
+: LifecycleNode("control_center", "autoware", options),
+ lease_duration_ms_{declare_parameter("lease_duration_ms")},
+ report_publish_rate_{declare_parameter("report_publish_rate")}
+{
+ RCLCPP_DEBUG(get_logger(), "ControlCenter is initialized");
+
+ timer_publish_reports_ = create_wall_timer(
+ std::chrono::duration(1.0 / report_publish_rate_),
+ std::bind(&ControlCenter::on_tick_publish_reports, this));
+
+ callback_group_mut_ex_ = create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
+
+ using std::placeholders::_1;
+ using std::placeholders::_2;
+ srv_register_ = create_service(
+ "~/srv/register", std::bind(&ControlCenter::on_register_node, this, _1, _2),
+ rmw_qos_profile_services_default, callback_group_mut_ex_);
+ srv_deregister_ = create_service(
+ "~/srv/deregister", std::bind(&ControlCenter::on_deregister_node, this, _1, _2),
+ rmw_qos_profile_services_default, callback_group_mut_ex_);
+ srv_report_state_ = create_service(
+ "~/srv/report_state", std::bind(&ControlCenter::on_report_state, this, _1, _2),
+ rmw_qos_profile_services_default, callback_group_mut_ex_);
+
+ pub_reports_ = create_publisher("~/reports", 1);
+
+ acc_uuid_ = autoware_utils::generate_uuid();
+}
+
+void ControlCenter::on_register_node(
+ const Register::Request::SharedPtr request, const Register::Response::SharedPtr response)
+{
+ RCLCPP_DEBUG(get_logger(), "on_register_node is called from %s", request->name_node.c_str());
+
+ std::optional node_uuid =
+ node_registry_.register_node(request->name_node, autoware_utils::generate_uuid());
+
+ if (node_uuid == std::nullopt) {
+ response->uuid_node = autoware_utils::generate_default_uuid();
+ response->status.status = Register::Response::_status_type::FAILURE;
+ } else {
+ NodeState un_state;
+ un_state.status = NodeState::UNKNOWN;
+ // alive, last_heartbeat, node_report, state
+ node_status_map_.insert({request->name_node, {false, this->now(), "", un_state}});
+ // Create heartbeat sub
+ rclcpp::Subscription::SharedPtr
+ heartbeat_node_sub = create_heartbeat_sub(request->name_node);
+ heartbeat_sub_map_.insert({request->name_node, heartbeat_node_sub});
+ RCLCPP_DEBUG(get_logger(), "Subscribed to topic %s", heartbeat_node_sub->get_topic_name());
+ response->uuid_node = node_uuid.value();
+ response->status.status = Register::Response::_status_type::SUCCESS;
+ }
+}
+
+void ControlCenter::on_deregister_node(
+ const Deregister::Request::SharedPtr request, const Deregister::Response::SharedPtr response)
+{
+ RCLCPP_DEBUG(get_logger(), "on_deregister_node is called from %s", request->name_node.c_str());
+
+ std::optional node_uuid =
+ node_registry_.deregister_node(request->name_node);
+
+ if (node_uuid == std::nullopt) {
+ response->uuid_node = autoware_utils::generate_default_uuid();
+ response->status.status = Deregister::Response::_status_type::FAILURE;
+ } else {
+ response->uuid_node = node_uuid.value();
+ response->status.status = Deregister::Response::_status_type::SUCCESS;
+ }
+}
+
+rclcpp::Subscription::SharedPtr ControlCenter::create_heartbeat_sub(
+ const std::string & node_name)
+{
+ RCLCPP_DEBUG(get_logger(), "Create heart sub is called.");
+ auto qos_profile = rclcpp::QoS(10);
+ qos_profile.liveliness(RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC)
+ .liveliness_lease_duration(lease_duration_ms_);
+
+ rclcpp::SubscriptionOptions heartbeat_sub_options;
+ std::function bound_liveliness_callback_func =
+ std::bind(&ControlCenter::liveliness_callback, this, std::placeholders::_1, node_name);
+ heartbeat_sub_options.event_callbacks.liveliness_callback = bound_liveliness_callback_func;
+
+ const std::string topic_name = node_name + "/heartbeat";
+ RCLCPP_DEBUG(get_logger(), "Topic to subscribe is %s", topic_name.c_str());
+ rclcpp::Subscription::SharedPtr heartbeat_sub;
+ std::function bound_heartbeat_callback_func =
+ std::bind(&ControlCenter::heartbeat_callback, this, std::placeholders::_1, node_name);
+ heartbeat_sub = create_subscription(
+ topic_name, qos_profile, bound_heartbeat_callback_func, heartbeat_sub_options);
+
+ return heartbeat_sub;
+}
+
+void ControlCenter::on_tick_publish_reports()
+{
+ NodeReports msg;
+ const rclcpp::Time stamp = this->now();
+ msg.stamp = stamp;
+ for (auto const & [name, info] : node_status_map_) {
+ autoware_control_center_msgs::msg::NodeReport report;
+ report.uuid_node = node_registry_.get_uuid(name).value();
+ report.name_node = name;
+ report.alive = info.alive;
+ report.last_node_state = info.state;
+ report.node_report = info.node_report;
+ report.last_heartbeat = stamp - info.last_heartbeat;
+ msg.nodes.push_back(report);
+ }
+ pub_reports_->publish(msg);
+}
+
+void ControlCenter::on_report_state(
+ const ReportState::Request::SharedPtr request, const ReportState::Response::SharedPtr response)
+{
+ RCLCPP_DEBUG(get_logger(), "on_report_state is called from %s", request->name_node.c_str());
+ // node_report
+ if (node_registry_.is_registered(request->name_node)) {
+ node_status_map_[request->name_node].node_report = request->message;
+ node_status_map_[request->name_node].state = request->state;
+
+ response->status.status = ReportState::Response::_status_type::SUCCESS;
+ response->uuid_node = node_registry_.get_uuid(request->name_node).value();
+ } else {
+ response->status.status = ReportState::Response::_status_type::FAILURE;
+ response->uuid_node = autoware_utils::generate_default_uuid();
+ response->log_response = request->name_node + " node was not registered.";
+ }
+}
+
+void ControlCenter::liveliness_callback(
+ rclcpp::QOSLivelinessChangedInfo & event, const std::string & node_name)
+{
+ RCLCPP_DEBUG(get_logger(), "Reader Liveliness changed event:");
+ RCLCPP_DEBUG(get_logger(), " alive_count: %d", event.alive_count);
+ RCLCPP_DEBUG(get_logger(), " not_alive_count: %d", event.not_alive_count);
+ RCLCPP_DEBUG(get_logger(), " alive_count_change: %d", event.alive_count_change);
+ RCLCPP_DEBUG(get_logger(), " not_alive_count_change: %d", event.not_alive_count_change);
+ if (event.alive_count == 0) {
+ RCLCPP_ERROR(get_logger(), "Heartbeat was not received");
+ node_status_map_[node_name].alive = false;
+ }
+}
+
+void ControlCenter::heartbeat_callback(
+ const Heartbeat::SharedPtr msg, const std::string & node_name)
+{
+ RCLCPP_DEBUG(get_logger(), "Watchdog raised, heartbeat sent at [%d.x]", msg->stamp.sec);
+ node_status_map_[node_name].alive = true;
+ node_status_map_[node_name].last_heartbeat = msg->stamp;
+}
+
+} // namespace autoware::control_center
+
+#include
+RCLCPP_COMPONENTS_REGISTER_NODE(autoware::control_center::ControlCenter)
diff --git a/common/autoware_control_center/src/include/control_center_node.hpp b/common/autoware_control_center/src/include/control_center_node.hpp
new file mode 100644
index 00000000..e002ce7c
--- /dev/null
+++ b/common/autoware_control_center/src/include/control_center_node.hpp
@@ -0,0 +1,100 @@
+// Copyright 2024 The Autoware Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CONTROL_CENTER_NODE_HPP_
+#define CONTROL_CENTER_NODE_HPP_
+
+#include "node_registry.hpp"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include