ADD: adden more functionality includeing a tcp server for external comms
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -7,3 +7,6 @@
|
||||
[submodule "build/libs/SimCore/libs/whisper-comm"]
|
||||
path = build/libs/SimCore/libs/whisper-comm
|
||||
url = ssh://git@dev-gitea.ftewa.ti.unibw-hamburg.de:12000/hwinkel/whisper-com.git
|
||||
[submodule "libs/OrderLibrary"]
|
||||
path = libs/OrderLibrary
|
||||
url = ssh://git@dev-gitea.ftewa.ti.unibw-hamburg.de:12000/hwinkel/OrderLibrary.git
|
||||
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -8,8 +8,8 @@
|
||||
"type": "gdb",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"target": "./build/SimControlApplication",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"target": "./SimControlApplication",
|
||||
"cwd": "${workspaceRoot}/build",
|
||||
"valuesFormatting": "parseText"
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,12 @@ include(defaultOptions)
|
||||
|
||||
#find_package(Eigen3 REQUIRED NO_MODULE)
|
||||
|
||||
|
||||
IF(NOT TARGET Catch2)
|
||||
add_subdirectory(libs/Catch2 EXCLUDE_FROM_ALL)
|
||||
include(libs/Catch2/contrib/Catch.cmake)
|
||||
ENDIF()
|
||||
|
||||
IF (NOT TARGET CLI11)
|
||||
set(CLI11_TESTING OFF CACHE BOOL "disable testing")
|
||||
add_subdirectory(libs/CLI11 EXCLUDE_FROM_ALL)
|
||||
@@ -17,18 +23,25 @@ IF(NOT TARGET loguru)
|
||||
ENDIF()
|
||||
|
||||
|
||||
IF(NOT TARGET EntityLibrary)
|
||||
add_subdirectory(libs/EntityLibrary EXCLUDE_FROM_ALL)
|
||||
IF(NOT TARGET SimCore)
|
||||
add_subdirectory(libs/SimCore EXCLUDE_FROM_ALL)
|
||||
ENDIF()
|
||||
|
||||
IF(NOT TARGET KubeControl)
|
||||
add_subdirectory(libs/KubeControl EXCLUDE_FROM_ALL)
|
||||
ENDIF()
|
||||
|
||||
IF(NOT TARGET OrderLibrary)
|
||||
add_subdirectory(libs/OrderLibrary EXCLUDE_FROM_ALL)
|
||||
ENDIF()
|
||||
|
||||
|
||||
|
||||
add_library(SimControl STATIC
|
||||
|
||||
include/SimControl/SimControl.hpp
|
||||
src/SimControl/SimControl.cpp
|
||||
|
||||
include/SimControl/PodList.hpp
|
||||
src/SimControl/PodList.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -36,8 +49,10 @@ target_link_libraries(SimControl
|
||||
PUBLIC
|
||||
CLI11
|
||||
pthread
|
||||
EntityLibrary
|
||||
SimCore
|
||||
kubecontrol
|
||||
loguru
|
||||
OrderLibrary
|
||||
)
|
||||
|
||||
target_include_directories(SimControl
|
||||
@@ -61,7 +76,7 @@ target_link_libraries(SimControlApplication
|
||||
|
||||
add_custom_command(TARGET SimControlApplication POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/EntityLibrary/libs/KubeControl/docs $<TARGET_FILE_DIR:${PROJECT_NAME}>/docs)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libs/KubeControl/docs $<TARGET_FILE_DIR:${PROJECT_NAME}>/docs)
|
||||
|
||||
# target_include_directories(EntitiyManagerApplication
|
||||
# PRIVATE
|
||||
@@ -70,4 +85,16 @@ add_custom_command(TARGET SimControlApplication POST_BUILD
|
||||
#
|
||||
# Everything TEST related
|
||||
#
|
||||
option(TEST_SIMCONTROLLER_LIBRARY "Turn running of application template specific tests off" ON)
|
||||
|
||||
IF (${TEST_SIMCONTROLLER_LIBRARY})
|
||||
|
||||
|
||||
|
||||
add_executable(test_podlist tests/test_podlist.cpp)
|
||||
target_link_libraries(test_podlist Catch2::Catch2 SimControl loguru)
|
||||
catch_discover_tests(test_podlist)
|
||||
|
||||
|
||||
|
||||
ENDIF()
|
||||
|
||||
16
Dockerfile
16
Dockerfile
@@ -16,14 +16,14 @@ RUN apt-get upgrade
|
||||
RUN apt-get -y install libsodium23 libuuid1 libstdc++5 libcurl4
|
||||
|
||||
# copy all custom buld libs
|
||||
COPY build/libs/EntityLibrary/libs/SimCore/libs/whisper-com/libs/libzmq/lib/libzmq.so.5 /usr/lib/
|
||||
COPY build/libs/SimCore/libs/whisper-com/libs/libzmq/lib/libzmq.so.5 /usr/lib/
|
||||
# COPY build/libs/EntityLibrary/libs/SimCore/libs/whisper-com/libs/protobuf/libprotobuf.so.32 /usr/lib/
|
||||
COPY build/libs/EntityLibrary/libs/SimCore/libs/whisper-com/libs/protobuf/libprotobufd.so.32 /usr/lib/
|
||||
COPY build/libs/EntityLibrary/libs/SimCore/libs/geographiclib/src/libGeographicLib.so.23 /usr/lib/
|
||||
COPY build/libs/EntityLibrary/libs/KubeControl/libs/yaml-cpp/libyaml-cpp.so.0.7 /usr/lib/
|
||||
COPY build/libs/EntityLibrary/libs/KubeControl/libs/curlpp/libcurlpp.so.1 /usr/lib/
|
||||
COPY build/libs/SimCore/libs/whisper-com/libs/protobuf/libprotobufd.so.32 /usr/lib/
|
||||
COPY build/libs/SimCore/libs/geographiclib/src/libGeographicLib.so.23 /usr/lib/
|
||||
COPY build/libs/KubeControl/libs/yaml-cpp/libyaml-cpp.so.0.8 /usr/lib/
|
||||
COPY build/libs/KubeControl/libs/curlpp/libcurlpp.so.1 /usr/lib/
|
||||
# COPY build/libs/EntityLibrary/libs/SimCore/libs/crossguid/libcrossguid.so.0 /usr/lib/
|
||||
COPY build/libs/EntityLibrary/libs/SimCore/libs/crossguid/libcrossguid-dgb.so.0 /usr/lib/
|
||||
COPY build/libs/SimCore/libs/crossguid/libcrossguid-dgb.so.0 /usr/lib/
|
||||
|
||||
|
||||
# Copy the current folder which contains C++ source code to the Docker image under /usr/src
|
||||
@@ -37,6 +37,6 @@ WORKDIR /usr/local/bin/
|
||||
# RUN clang++ -o Test Test.cpp
|
||||
|
||||
# Run the output program from the previous step
|
||||
CMD ["SimControlApplication"]
|
||||
# CMD ["SimControlApplication"]
|
||||
|
||||
# CMD ["bin/bash"]
|
||||
CMD ["bin/bash"]
|
||||
@@ -15,7 +15,7 @@ function(protobuf_generate_cpp)
|
||||
${ARGN})
|
||||
|
||||
FILE(GLOB PROTO_FILES ${protobuf_PROTO_PATH}/*.proto)
|
||||
set(PROTOC ${CMAKE_BINARY_DIR}/libs/EntityLibrary/libs/SimCore/libs/whisper-com/libs/protobuf/protoc)
|
||||
set(PROTOC ${CMAKE_BINARY_DIR}/libs/SimCore/libs/whisper-com/libs/protobuf/protoc)
|
||||
message(STATUS "protoc path: " ${PROTOC})
|
||||
|
||||
FOREACH(proto ${PROTO_FILES})
|
||||
@@ -28,7 +28,7 @@ function(protobuf_generate_cpp)
|
||||
add_custom_command(
|
||||
OUTPUT "${protoDIR}/${protoFILENAME}.pb.cc"
|
||||
DEPENDS "${protoDIR}/${protoFILENAME}.proto"
|
||||
COMMAND ${PROTOC} --cpp_out=${protoDIR} --proto_path=${protoDIR} --proto_path="${CMAKE_SOURCE_DIR}/libs/EntityLibrary/libs/SimCore/include/SimCore/Messages/Protos" --proto_path="${CMAKE_SOURCE_DIR}/libs/EntityLibrary/libs/SimCore/libs/whisper-com/libs/protobuf/src" "${protoDIR}/${protoFILENAME}.proto"
|
||||
COMMAND ${PROTOC} --cpp_out=${protoDIR} --proto_path=${protoDIR} --proto_path="${CMAKE_SOURCE_DIR}/libs/SimCore/include/SimCore/Messages/Protos" --proto_path="${CMAKE_SOURCE_DIR}/libs/SimCore/libs/whisper-com/libs/protobuf/src" "${protoDIR}/${protoFILENAME}.proto"
|
||||
|
||||
)
|
||||
|
||||
|
||||
47
docs/info.json
Normal file
47
docs/info.json
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{"apiVersion":"v1","kind":"Pod","metadata":{"creationTimestamp":"2023-08-11T11:36:22Z","labels":{"app.kubernetes.io/name":"hamburg"},"managedFields":[{"apiVersion":"v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app.kubernetes.io/name":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"hamburg-container\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"COMMAND_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"ENTITY_ID\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"ENTITY_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"GROUNDTRUTH_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"POS_HEIGHT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"POS_LAT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"POS_LONG\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:enableServiceLinks":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}},
|
||||
"manager":"unknown","operation":"Update","time":"2023-08-11T11:36:22Z"},
|
||||
{"apiVersion":"v1","fieldsType":"FieldsV1",
|
||||
"fieldsV1":{"f:status":{"f:conditions":{"k:{\"type\":\"ContainersReady\"}":{".":{},"f:lastProbeTime":{},"f:lastTransitionTime":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Initialized\"}":{".":{},"f:lastProbeTime":{},"f:lastTransitionTime":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Ready\"}":{".":{},"f:lastProbeTime":{},"f:lastTransitionTime":{},"f:status":{},"f:type":{}}},
|
||||
"f:containerStatuses":{},"f:hostIP":{},"f:phase":{},"f:podIP":{},
|
||||
"f:podIPs":{".":{},"k:{\"ip\":\"10.0.3.98\"}":{".":{},"f:ip":{}}},"f:startTime":{}}},
|
||||
"manager":"kubelet","operation":"Update","subresource":"status","time":"2023-08-11T11:36:23Z"}],
|
||||
"name":"ship1","namespace":"hwinkel","resourceVersion":"171734008","uid":"68ef8a8c-33c9-4160-89ea-7f51d5e6735a"},
|
||||
"spec":{"containers":[{"env":[{"name":"COMMAND_PORT","value":"\"5555\""},
|
||||
{"name":"ENTITY_ID","value":"ship1"},
|
||||
{"name":"ENTITY_NAME","value":"hamburg"},
|
||||
{"name":"GROUNDTRUTH_PORT","value":"\"8000\""},
|
||||
{"name":"POS_HEIGHT","value":"\"0\""},
|
||||
{"name":"POS_LAT","value":"\"55\""},
|
||||
{"name":"POS_LONG","value":"\"8\""}],
|
||||
"image":"kmaster.ti.unibw-hamburg.de:30808/ship:latest",
|
||||
"imagePullPolicy":"Always",
|
||||
"name":"hamburg-container",
|
||||
"resources":{},
|
||||
"terminationMessagePath":"/dev/termination-log",
|
||||
"terminationMessagePolicy":"File",
|
||||
"volumeMounts":[{"mountPath":"/var/run/secrets/kubernetes.io/serviceaccount","name":"kube-api-access-92lpt","readOnly":true}]}],
|
||||
"dnsPolicy":"ClusterFirst",
|
||||
"enableServiceLinks":true,
|
||||
"nodeName":"kslave02.ti.unibw-hamburg.de",
|
||||
"preemptionPolicy":"PreemptLowerPriority",
|
||||
"priority":0,
|
||||
"restartPolicy":"Never",
|
||||
"schedulerName":"default-scheduler",
|
||||
"securityContext":{},
|
||||
"serviceAccount":"default",
|
||||
"serviceAccountName":"default",
|
||||
"terminationGracePeriodSeconds":30,
|
||||
"tolerations":[
|
||||
{"effect":"NoExecute","key":"node.kubernetes.io/not-ready","operator":"Exists","tolerationSeconds":300},
|
||||
{"effect":"NoExecute","key":"node.kubernetes.io/unreachable","operator":"Exists","tolerationSeconds":300}],
|
||||
"volumes":[
|
||||
{"name":"kube-api-access-92lpt",
|
||||
"projected":{
|
||||
"defaultMode":420,"sources":[{"serviceAccountToken":{"expirationSeconds":3607,"path":"token"}},{"configMap":{"items":[{"key":"ca.crt","path":"ca.crt"}],"name":"kube-root-ca.crt"}},{"downwardAPI":{"items":[{"fieldRef":{"apiVersion":"v1","fieldPath":"metadata.namespace"},"path":"namespace"}]}}]}}]},"status":{"conditions":[{"lastProbeTime":null,"lastTransitionTime":"2023-08-11T11:36:22Z","status":"True","type":"Initialized"},{"lastProbeTime":null,"lastTransitionTime":"2023-08-11T11:36:23Z","status":"True","type":"Ready"},{"lastProbeTime":null,"lastTransitionTime":"2023-08-11T11:36:23Z","status":"True","type":"ContainersReady"},{"lastProbeTime":null,"lastTransitionTime":"2023-08-11T11:36:22Z","status":"True","type":"PodScheduled"}],"containerStatuses":[{"containerID":"cri-o://c2632fb622a89b099c566b83a81de28e665176ea4f91f8ad1b17b97945c9450a","image":"kmaster.ti.unibw-hamburg.de:30808/ship:latest","imageID":"kmaster.ti.unibw-hamburg.de:30808/ship@sha256:53032573b5777e4ec30764b4700aa5b7ec3ba5b9c035cdecf5c8abe8f0e30279","lastState":{},"name":"hamburg-container","ready":true,"restartCount":0,"started":true,"state":{"running":{"startedAt":"2023-08-11T11:36:23Z"}}}],"hostIP":"192.168.252.8","phase":"Running","podIP":"10.0.3.98","podIPs":[{"ip":"10.0.3.98"}],"qosClass":"BestEffort","startTime":"2023-08-11T11:36:22Z"}}
|
||||
64
include/SimControl/PodList.hpp
Normal file
64
include/SimControl/PodList.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "nlohmann/json_fwd.hpp"
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace SimControl
|
||||
{
|
||||
struct PodListItem
|
||||
{
|
||||
std::string Uuid;
|
||||
std::string Image;
|
||||
std::string Ip;
|
||||
std::string Component;
|
||||
std::string Status;
|
||||
std::string PartOf;
|
||||
std::vector<std::pair<std::string,std::shared_ptr<PodListItem>>> relatedPods;
|
||||
|
||||
bool operator==(PodListItem const &rhs )
|
||||
{
|
||||
if (Uuid == rhs.Uuid) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class PodList
|
||||
{
|
||||
public:
|
||||
PodList();
|
||||
PodList(std::string jsonstring);
|
||||
|
||||
|
||||
void addPodToList(std::shared_ptr<PodListItem> pod);
|
||||
void addPodToList(std::string respond);
|
||||
|
||||
std::shared_ptr<PodListItem> getPod(std::string uuid);
|
||||
|
||||
bool removePod(std::string uuid);
|
||||
|
||||
std::vector<std::string> getPodsUUID();
|
||||
|
||||
nlohmann::json getPodTree(std::string uuid);
|
||||
|
||||
size_t size();
|
||||
|
||||
private:
|
||||
|
||||
void addPodsFromString(std::string respond);
|
||||
|
||||
std::map<std::string, std::shared_ptr<PodListItem>> podList_;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __SIMCONTROL__
|
||||
|
||||
#include "DirectCommunicationClient.hpp"
|
||||
#include "DirectCommunicationServer.hpp"
|
||||
#include "SimCore/Identifier.hpp"
|
||||
#include "crossguid/guid.hpp"
|
||||
#include "kubecontrol/PodController.hpp"
|
||||
@@ -26,24 +27,33 @@ namespace SimControl {
|
||||
class SimControl{
|
||||
|
||||
public:
|
||||
SimControl(ushort CommandPort);
|
||||
SimControl(std::string CommandPort);
|
||||
~SimControl();
|
||||
void stop();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
const SimCore::Identifier ID_;
|
||||
ushort CommandPort_;
|
||||
std::string CommandPort_;
|
||||
void MainFunction_();
|
||||
|
||||
void HandleMessage(std::string msg);
|
||||
|
||||
void HandleExternalMessage(std::string msg);
|
||||
|
||||
|
||||
void startShip(std::string name);
|
||||
|
||||
|
||||
std::unique_ptr<kubecontrol::PodController> PodController_;
|
||||
|
||||
|
||||
std::thread MainThread_;
|
||||
std::atomic<bool> stopMainThread_ = false;
|
||||
|
||||
std::unique_ptr<DirectCommunication::DirectCommunicationClient> TCPClient_;
|
||||
std::shared_ptr<DirectCommunication::DirectCommunicationClient> TCPClient_ = nullptr;
|
||||
std::shared_ptr<DirectCommunication::DirectCommunicationServer> ExternalTCPServer_ = nullptr;
|
||||
|
||||
|
||||
|
||||
|
||||
1
libs/OrderLibrary
Submodule
1
libs/OrderLibrary
Submodule
Submodule libs/OrderLibrary added at 88aa0d0eff
171
src/SimControl/PodList.cpp
Normal file
171
src/SimControl/PodList.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <SimControl/PodList.hpp>
|
||||
#include <loguru.hpp>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
|
||||
|
||||
namespace SimControl
|
||||
{
|
||||
PodList::PodList()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
PodList::PodList(std::string jsonstring)
|
||||
{
|
||||
|
||||
addPodsFromString(jsonstring);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void PodList::addPodsFromString(std::string respond)
|
||||
{
|
||||
nlohmann::json j;
|
||||
try
|
||||
{
|
||||
j = nlohmann::json::parse(respond);
|
||||
if (j.contains("items"))
|
||||
{
|
||||
int i = j["items"].size();
|
||||
for (int a = 0; a<i; a++)
|
||||
{
|
||||
auto item = std::make_shared<PodListItem>();
|
||||
item->Uuid = j["items"][a]["metadata"]["labels"]["app.kubernetes.io/name"].get<std::string>();
|
||||
item->Component = j["items"][a]["metadata"]["labels"]["app.kubernetes.io/component"].get<std::string>();
|
||||
item->Image = j["items"][a]["spec"]["containers"][0]["image"].get<std::string>();
|
||||
item->Ip = j["items"][a]["status"]["podIP"].get<std::string>();
|
||||
item->Status = j["items"][a]["status"]["phase"].get<std::string>();
|
||||
item->PartOf = j["items"][a]["metadata"]["labels"]["app.kubernetes.io/part-of"].get<std::string>();
|
||||
// LOG_S(INFO)<<j["items"][a]["status"]["podIP"];
|
||||
|
||||
// LOG_S(INFO)<<j["items"][a]["status"]["phase"];
|
||||
this->addPodToList(item);
|
||||
}
|
||||
|
||||
|
||||
i = j["items"].size();
|
||||
for (int a = 0; a<i; a++)
|
||||
{
|
||||
auto item = std::make_shared<PodListItem>();
|
||||
std::string uuid = j["items"][a]["metadata"]["labels"]["app.kubernetes.io/name"].get<std::string>();
|
||||
std::string parentName= j["items"][a]["metadata"]["labels"]["app.kubernetes.io/part-of"].get<std::string>();
|
||||
|
||||
auto self = this->getPod(uuid);
|
||||
auto parent = this->getPod(parentName);
|
||||
if (parent != nullptr && self != nullptr) {
|
||||
parent->relatedPods.emplace_back(std::make_pair(self->Uuid, self));
|
||||
}else if (parent== nullptr)
|
||||
{
|
||||
self->PartOf = parentName;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (std::exception& e)
|
||||
{
|
||||
LOG_S(ERROR)<<e.what();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void PodList::addPodToList(std::shared_ptr<PodListItem> pod)
|
||||
{
|
||||
|
||||
podList_.insert_or_assign(pod->Uuid,pod);
|
||||
|
||||
}
|
||||
|
||||
void PodList::addPodToList(std::string respond)
|
||||
{
|
||||
|
||||
addPodsFromString(respond);
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::shared_ptr<PodListItem> PodList::getPod(std::string uuid)
|
||||
{
|
||||
for(auto item : podList_)
|
||||
{
|
||||
if (item.first == uuid)
|
||||
{
|
||||
return item.second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool PodList::removePod(std::string uuid)
|
||||
{
|
||||
for(auto it = podList_.begin(); it != podList_.end();)
|
||||
{
|
||||
if (it->first == uuid)
|
||||
{
|
||||
it = podList_.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> PodList::getPodsUUID()
|
||||
{
|
||||
std::vector<std::string> list;
|
||||
LOG_S(INFO)<< podList_.size();
|
||||
for(auto item : podList_)
|
||||
{
|
||||
list.emplace_back(item.first);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
nlohmann::json PodList::getPodTree(std::string uuid)
|
||||
{
|
||||
LOG_S(INFO)<<uuid;
|
||||
nlohmann::json j;
|
||||
auto pod = this->getPod(uuid);
|
||||
if (pod!= nullptr)
|
||||
{
|
||||
j["UUID"] = pod->Uuid;
|
||||
j["IMAGE"] = pod->Image;
|
||||
j["IP"] = pod->Ip;
|
||||
j["COMPONENT"] = pod->Component;
|
||||
|
||||
int i = 0;
|
||||
for (auto item:pod->relatedPods)
|
||||
{
|
||||
|
||||
j["RELATIVES"][i]["UUID"] = item.second->Uuid;
|
||||
j["RELATIVES"][i]["IMAGE"] = item.second->Image;
|
||||
j["RELATIVES"][i]["IP"] = item.second->Ip;
|
||||
j["RELATIVES"][i]["COMPONENT"] = item.second->Component;
|
||||
j["RELATIVES"][i]["PART-OF"] = item.second->PartOf;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return j;
|
||||
|
||||
}
|
||||
|
||||
size_t PodList::size()
|
||||
{
|
||||
return podList_.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,30 +1,39 @@
|
||||
#include "Orders/MoveOrder.hpp"
|
||||
#include "SimCore/Identifier.hpp"
|
||||
#include <Orders/MoveOrder.hpp>
|
||||
#include <SimCore/Identifier.hpp>
|
||||
#include "DirectCommunicationServer.hpp"
|
||||
#include "SimCore/Messages/Control.hpp"
|
||||
#include "SimCore/Messages/SimTrack.hpp"
|
||||
#include "SimCore/SimCore.hpp"
|
||||
#include "WHISPER/Messages/Message.hpp"
|
||||
#include "crossguid/guid.hpp"
|
||||
#include "kubecontrol/KubePod.hpp"
|
||||
#include "kubecontrol/PodController.hpp"
|
||||
#include "nlohmann/json_fwd.hpp"
|
||||
#include <SimControl/SimControl.hpp>
|
||||
|
||||
|
||||
|
||||
#include <fstream>
|
||||
#include <loguru.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
|
||||
|
||||
namespace SimControl {
|
||||
|
||||
|
||||
SimControl::SimControl( ushort CommandPort):CommandPort_(CommandPort),ID_(SimCore::Identifier(xg::newGuid()))
|
||||
SimControl::SimControl( std::string CommandPort):CommandPort_(CommandPort),ID_(SimCore::Identifier(xg::newGuid()))
|
||||
{
|
||||
PodController_ = std::make_unique<kubecontrol::PodController>("docs/config");
|
||||
|
||||
TCPClient_ = std::make_unique<DirectCommunication::DirectCommunicationClient>(30200,"192.168.252.6");
|
||||
TCPClient_->registerMessageCallback(std::bind(&SimControl::HandleMessage,this,std::placeholders::_1));
|
||||
TCPClient_->sendMessage("Hello Server");
|
||||
ExternalTCPServer_ = std::make_shared<DirectCommunication::DirectCommunicationServer>(5000);
|
||||
ExternalTCPServer_->registerMessageCallback(std::bind(&SimControl::HandleExternalMessage,this,std::placeholders::_1));
|
||||
// TCPClient_ = std::make_unique<DirectCommunication::DirectCommunicationClient>(30200,"192.168.252.6");
|
||||
// TCPClient_ = std::make_unique<DirectCommunication::DirectCommunicationClient>(5555,"127.0.0.1");
|
||||
|
||||
// TCPClient_->registerMessageCallback(std::bind(&SimControl::HandleMessage,this,std::placeholders::_1));
|
||||
// TCPClient_->sendMessage("Hello Server");
|
||||
|
||||
this->stopMainThread_ = false;
|
||||
MainThread_ = std::thread(&SimControl::MainFunction_,this);
|
||||
@@ -33,26 +42,28 @@ namespace SimControl {
|
||||
|
||||
SimControl::~SimControl()
|
||||
{
|
||||
stop();
|
||||
|
||||
stopMainThread_ = true;
|
||||
while (!MainThread_.joinable()) {
|
||||
// LOG_S(INFO)<< "waiting";
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
MainThread_.join();
|
||||
|
||||
this->stopMainThread_ = true;
|
||||
TCPClient_.reset();
|
||||
|
||||
TCPClient_ = nullptr;
|
||||
|
||||
}
|
||||
|
||||
void SimControl::stop()
|
||||
{
|
||||
PodController_->stopAllPods();
|
||||
this->stopMainThread_ = true;
|
||||
TCPClient_.reset();
|
||||
|
||||
|
||||
PodController_->stopAllPods();
|
||||
}
|
||||
|
||||
void SimControl::HandleMessage(std::string msg)
|
||||
{
|
||||
TCPClient_->sendMessage("Hello Server") ;
|
||||
// LOG_S(INFO)<<msg;
|
||||
auto simtrack = SimCore::SimTrack::unpack(msg);
|
||||
if (simtrack != nullptr)
|
||||
@@ -69,37 +80,52 @@ namespace SimControl {
|
||||
}
|
||||
|
||||
|
||||
void SimControl::HandleExternalMessage(std::string msg)
|
||||
{
|
||||
|
||||
LOG_S(INFO)<<msg;
|
||||
}
|
||||
|
||||
|
||||
void SimControl::startShip(std::string name)
|
||||
{
|
||||
std::string uuid = name;
|
||||
kubecontrol::KubePod ShipPod1("controller",uuid,"ship","ship:latest");
|
||||
ShipPod1.setEnvironmentVar("ENTITY_ID", uuid);
|
||||
ShipPod1.setEnvironmentVar("ENTITY_NAME", "hamburg");
|
||||
ShipPod1.setEnvironmentVar("POS_LAT", "\"55\"");
|
||||
ShipPod1.setEnvironmentVar("POS_LONG", "\"8\"");
|
||||
ShipPod1.setEnvironmentVar("POS_HEIGHT", "\"0\"");
|
||||
ShipPod1.setEnvironmentVar("GROUNDTRUTH_PORT", "\"8000\"");
|
||||
ShipPod1.setEnvironmentVar("COMMAND_PORT", "\""+CommandPort_+"\"");
|
||||
|
||||
ShipPod1.createYAML();
|
||||
PodController_->startPod(ShipPod1);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void SimControl::MainFunction_()
|
||||
{
|
||||
// std::string uuid = xg::newGuid().str();
|
||||
std::string uuid = "ship1";
|
||||
kubecontrol::KubePod ShipPod1("hamburg",uuid,"ship:latest");
|
||||
ShipPod1.setEnvironmentVar("ENTITY_ID", "ship1");
|
||||
ShipPod1.setEnvironmentVar("ENTITY_NAME", "hamburg");
|
||||
ShipPod1.setEnvironmentVar("POS_LAT", "\"55\"");
|
||||
ShipPod1.setEnvironmentVar("POS_LONG", "\"8\"");
|
||||
ShipPod1.setEnvironmentVar("POS_HEIGHT", "\"0\"");
|
||||
ShipPod1.setEnvironmentVar("GROUNDTRUTH_PORT", "\"8000\"");
|
||||
ShipPod1.setEnvironmentVar("COMMAND_PORT", "\"5555\"");
|
||||
|
||||
startShip("hamburg");
|
||||
|
||||
ShipPod1.createYAML();
|
||||
PodController_->startPod(ShipPod1);
|
||||
LOG_S(INFO)<<PodController_->getInfoForPod("hamburg");
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
|
||||
LOG_S(INFO)<<PodController_->getInfoForPod("hamburg");
|
||||
// LOG_S(INFO)<<PodController_->getPodsInfo();
|
||||
auto IP = PodController_->getPodsInfo("hamburg")->Ip;
|
||||
TCPClient_ = std::make_shared<DirectCommunication::DirectCommunicationClient>(5555,IP);
|
||||
// Orders::MoveOrder order(this->ID_);
|
||||
// order.Speed.setValue(0);
|
||||
// TCPClient_->sendMessage(order.buildMessage().serialize());
|
||||
|
||||
|
||||
Orders::MoveOrder order(this->ID_);
|
||||
order.Speed.setValue(0);
|
||||
TCPClient_->sendMessage(order.buildMessage().serialize());
|
||||
|
||||
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(3000));
|
||||
// TCPClient_->disconnect();
|
||||
// TCPClient_.reset();
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(10000));
|
||||
// SimCore::Control shutdown(this->ID_,SimCore::SHUT_DOWN,"");
|
||||
// TCPClient_->sendMessage(shutdown.buildMessage().serialize());
|
||||
// // TCPClient_->disconnect();
|
||||
// // TCPClient_.reset();
|
||||
|
||||
while (!stopMainThread_)
|
||||
{
|
||||
@@ -107,8 +133,8 @@ namespace SimControl {
|
||||
|
||||
|
||||
}
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(3000));
|
||||
TCPClient_->disconnect();
|
||||
|
||||
|
||||
LOG_S(INFO)<<"main func stopped";
|
||||
}
|
||||
|
||||
|
||||
14
src/main.cpp
14
src/main.cpp
@@ -1,4 +1,4 @@
|
||||
#include "Orders/MoveOrder.hpp"
|
||||
#include <Orders/MoveOrder.hpp>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <csignal>
|
||||
@@ -33,11 +33,10 @@ int main()
|
||||
sigIntHandler.sa_flags = 0;
|
||||
sigaction(SIGINT, &sigIntHandler, NULL);
|
||||
|
||||
const char* CommandPortChar = "31371";
|
||||
const char* CommandPortChar = "5555";
|
||||
if (std::getenv("COMMAND_PORT") != nullptr) {
|
||||
CommandPortChar = std::getenv("COMMAND_PORT");
|
||||
}
|
||||
ushort commandPort_ = (unsigned short)strtol(CommandPortChar,NULL,0);
|
||||
|
||||
|
||||
const char *ServerAddress = nullptr;
|
||||
@@ -49,21 +48,20 @@ int main()
|
||||
|
||||
LOG_S(INFO)<<ServerAddress;
|
||||
|
||||
ushort port = 8000;
|
||||
SimControl::SimControl sc(commandPort_);
|
||||
SimControl::SimControl sc(CommandPortChar);
|
||||
|
||||
|
||||
|
||||
while (running) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
sc.stop();
|
||||
|
||||
LOG_S(INFO)<<"end app";
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(300));
|
||||
return 0;
|
||||
std::exit(0);
|
||||
// std::exit(0);
|
||||
}
|
||||
267
tests/info.json
Normal file
267
tests/info.json
Normal file
@@ -0,0 +1,267 @@
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"creationTimestamp": "2023-08-14T14:05:26Z",
|
||||
"labels": {
|
||||
"app.kubernetes.io/name": "hamburg"
|
||||
},
|
||||
"managedFields": [{
|
||||
"apiVersion": "v1",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:metadata": {
|
||||
"f:labels": {
|
||||
".": {},
|
||||
"f:app.kubernetes.io/name": {}
|
||||
}
|
||||
},
|
||||
"f:spec": {
|
||||
"f:containers": {
|
||||
"k:{\"name\":\"hamburg-container\"}": {
|
||||
".": {},
|
||||
"f:env": {
|
||||
".": {},
|
||||
"k:{\"name\":\"COMMAND_PORT\"}": {
|
||||
".": {},
|
||||
"f:name": {},
|
||||
"f:value": {}
|
||||
},
|
||||
"k:{\"name\":\"ENTITY_ID\"}": {
|
||||
".": {},
|
||||
"f:name": {},
|
||||
"f:value": {}
|
||||
},
|
||||
"k:{\"name\":\"ENTITY_NAME\"}": {
|
||||
".": {},
|
||||
"f:name": {},
|
||||
"f:value": {}
|
||||
},
|
||||
"k:{\"name\":\"GROUNDTRUTH_PORT\"}": {
|
||||
".": {},
|
||||
"f:name": {},
|
||||
"f:value": {}
|
||||
},
|
||||
"k:{\"name\":\"POS_HEIGHT\"}": {
|
||||
".": {},
|
||||
"f:name": {},
|
||||
"f:value": {}
|
||||
},
|
||||
"k:{\"name\":\"POS_LAT\"}": {
|
||||
".": {},
|
||||
"f:name": {},
|
||||
"f:value": {}
|
||||
},
|
||||
"k:{\"name\":\"POS_LONG\"}": {
|
||||
".": {},
|
||||
"f:name": {},
|
||||
"f:value": {}
|
||||
}
|
||||
},
|
||||
"f:image": {},
|
||||
"f:imagePullPolicy": {},
|
||||
"f:name": {},
|
||||
"f:resources": {},
|
||||
"f:terminationMessagePath": {},
|
||||
"f:terminationMessagePolicy": {}
|
||||
}
|
||||
},
|
||||
"f:dnsPolicy": {},
|
||||
"f:enableServiceLinks": {},
|
||||
"f:restartPolicy": {},
|
||||
"f:schedulerName": {},
|
||||
"f:securityContext": {},
|
||||
"f:terminationGracePeriodSeconds": {}
|
||||
}
|
||||
},
|
||||
"manager": "unknown",
|
||||
"operation": "Update",
|
||||
"time": "2023-08-14T14:05:26Z"
|
||||
}, {
|
||||
"apiVersion": "v1",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:status": {
|
||||
"f:conditions": {
|
||||
"k:{\"type\":\"ContainersReady\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Initialized\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
},
|
||||
"k:{\"type\":\"Ready\"}": {
|
||||
".": {},
|
||||
"f:lastProbeTime": {},
|
||||
"f:lastTransitionTime": {},
|
||||
"f:status": {},
|
||||
"f:type": {}
|
||||
}
|
||||
},
|
||||
"f:containerStatuses": {},
|
||||
"f:hostIP": {},
|
||||
"f:phase": {},
|
||||
"f:podIP": {},
|
||||
"f:podIPs": {
|
||||
".": {},
|
||||
"k:{\"ip\":\"10.0.3.47\"}": {
|
||||
".": {},
|
||||
"f:ip": {}
|
||||
}
|
||||
},
|
||||
"f:startTime": {}
|
||||
}
|
||||
},
|
||||
"manager": "kubelet",
|
||||
"operation": "Update",
|
||||
"subresource": "status",
|
||||
"time": "2023-08-14T14:05:28Z"
|
||||
}],
|
||||
"name": "ship1",
|
||||
"namespace": "hwinkel",
|
||||
"resourceVersion": "172370869",
|
||||
"uid": "d66ba1fc-f664-4d88-acd0-f33bbc936d7c"
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"env": [{
|
||||
"name": "COMMAND_PORT",
|
||||
"value": "\"5555\""
|
||||
}, {
|
||||
"name": "ENTITY_ID",
|
||||
"value": "ship1"
|
||||
}, {
|
||||
"name": "ENTITY_NAME",
|
||||
"value": "hamburg"
|
||||
}, {
|
||||
"name": "GROUNDTRUTH_PORT",
|
||||
"value": "\"8000\""
|
||||
}, {
|
||||
"name": "POS_HEIGHT",
|
||||
"value": "\"0\""
|
||||
}, {
|
||||
"name": "POS_LAT",
|
||||
"value": "\"55\""
|
||||
}, {
|
||||
"name": "POS_LONG",
|
||||
"value": "\"8\""
|
||||
}],
|
||||
"image": "kmaster.ti.unibw-hamburg.de:30808/ship:latest",
|
||||
"imagePullPolicy": "Always",
|
||||
"name": "hamburg-container",
|
||||
"resources": {},
|
||||
"terminationMessagePath": "/dev/termination-log",
|
||||
"terminationMessagePolicy": "File",
|
||||
"volumeMounts": [{
|
||||
"mountPath": "/var/run/secrets/kubernetes.io/serviceaccount",
|
||||
"name": "kube-api-access-tpw7t",
|
||||
"readOnly": true
|
||||
}]
|
||||
}],
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"enableServiceLinks": true,
|
||||
"nodeName": "kslave02.ti.unibw-hamburg.de",
|
||||
"preemptionPolicy": "PreemptLowerPriority",
|
||||
"priority": 0,
|
||||
"restartPolicy": "Never",
|
||||
"schedulerName": "default-scheduler",
|
||||
"securityContext": {},
|
||||
"serviceAccount": "default",
|
||||
"serviceAccountName": "default",
|
||||
"terminationGracePeriodSeconds": 30,
|
||||
"tolerations": [{
|
||||
"effect": "NoExecute",
|
||||
"key": "node.kubernetes.io/not-ready",
|
||||
"operator": "Exists",
|
||||
"tolerationSeconds": 300
|
||||
}, {
|
||||
"effect": "NoExecute",
|
||||
"key": "node.kubernetes.io/unreachable",
|
||||
"operator": "Exists",
|
||||
"tolerationSeconds": 300
|
||||
}],
|
||||
"volumes": [{
|
||||
"name": "kube-api-access-tpw7t",
|
||||
"projected": {
|
||||
"defaultMode": 420,
|
||||
"sources": [{
|
||||
"serviceAccountToken": {
|
||||
"expirationSeconds": 3607,
|
||||
"path": "token"
|
||||
}
|
||||
}, {
|
||||
"configMap": {
|
||||
"items": [{
|
||||
"key": "ca.crt",
|
||||
"path": "ca.crt"
|
||||
}],
|
||||
"name": "kube-root-ca.crt"
|
||||
}
|
||||
}, {
|
||||
"downwardAPI": {
|
||||
"items": [{
|
||||
"fieldRef": {
|
||||
"apiVersion": "v1",
|
||||
"fieldPath": "metadata.namespace"
|
||||
},
|
||||
"path": "namespace"
|
||||
}]
|
||||
}
|
||||
}]
|
||||
}
|
||||
}]
|
||||
},
|
||||
"status": {
|
||||
"conditions": [{
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-08-14T14:05:26Z",
|
||||
"status": "True",
|
||||
"type": "Initialized"
|
||||
}, {
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-08-14T14:05:28Z",
|
||||
"status": "True",
|
||||
"type": "Ready"
|
||||
}, {
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-08-14T14:05:28Z",
|
||||
"status": "True",
|
||||
"type": "ContainersReady"
|
||||
}, {
|
||||
"lastProbeTime": null,
|
||||
"lastTransitionTime": "2023-08-14T14:05:26Z",
|
||||
"status": "True",
|
||||
"type": "PodScheduled"
|
||||
}],
|
||||
"containerStatuses": [{
|
||||
"containerID": "cri-o://8da2c28ef57a276017f9ca1e840d8387006343cdb1f90f393a7eb3dbff43cdca",
|
||||
"image": "kmaster.ti.unibw-hamburg.de:30808/ship:latest",
|
||||
"imageID": "kmaster.ti.unibw-hamburg.de:30808/ship@sha256:842cf35f29e1e8139a990b02ec7d64195307f789e03e52e7fb3b8c9cfb13ded5",
|
||||
"lastState": {},
|
||||
"name": "hamburg-container",
|
||||
"ready": true,
|
||||
"restartCount": 0,
|
||||
"started": true,
|
||||
"state": {
|
||||
"running": {
|
||||
"startedAt": "2023-08-14T14:05:27Z"
|
||||
}
|
||||
}
|
||||
}],
|
||||
"hostIP": "192.168.252.8",
|
||||
"phase": "Running",
|
||||
"podIP": "10.0.3.47",
|
||||
"podIPs": [{
|
||||
"ip": "10.0.3.47"
|
||||
}],
|
||||
"qosClass": "BestEffort",
|
||||
"startTime": "2023-08-14T14:05:26Z"
|
||||
}
|
||||
}
|
||||
1148
tests/string.cpp
Normal file
1148
tests/string.cpp
Normal file
File diff suppressed because it is too large
Load Diff
77
tests/test_podlist.cpp
Normal file
77
tests/test_podlist.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
|
||||
|
||||
#include "kubecontrol/PodController.hpp"
|
||||
#include "nlohmann/json_fwd.hpp"
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "SimControl/PodList.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <loguru.hpp>
|
||||
|
||||
#include "string.cpp"
|
||||
|
||||
// SimCore::Identifier OwnID, SimCore::Identifier ParentID, SimCore::SensorKinds SensorKind,std::uint32_t GroundTruthPort, std::uint32_t ParentPort,std::string ParentIPAddress
|
||||
|
||||
SCENARIO("Testing the the podlist")
|
||||
{
|
||||
|
||||
auto control = kubecontrol::PodController("docs/config");
|
||||
|
||||
|
||||
|
||||
|
||||
// std::ifstream ifs("info.json");
|
||||
nlohmann::json j = nlohmann::json::parse(control.getPodsInfo());
|
||||
|
||||
LOG_S(INFO)<<control.getInfoForPod("hamburg");
|
||||
// LOG_S(INFO)<< j;
|
||||
|
||||
LOG_S(INFO)<<control.getPodsInfoForAll();
|
||||
|
||||
|
||||
// SimControl::PodList podlist(j.dump());
|
||||
|
||||
|
||||
// auto info = control.getInfoForPod("20939877-4c5e-4dbc-9f62-07533195334e");
|
||||
// LOG_S(INFO)<<info;
|
||||
// podlist.addPodToList(info);
|
||||
// LOG_S(INFO)<<podlist.getPodTree("hamburg").dump();
|
||||
|
||||
|
||||
// auto item1 = std::make_shared<SimControl::PodListItem>();
|
||||
|
||||
// auto item2 = std::make_shared<SimControl::PodListItem>();
|
||||
// item2->Uuid= "movement";
|
||||
|
||||
|
||||
// item1->Uuid= "test";
|
||||
// item1->relatedPods.emplace_back(item2);
|
||||
|
||||
|
||||
// podlist.addPodToList(item1);
|
||||
// podlist.addPodToList(item2);
|
||||
|
||||
|
||||
|
||||
// LOG_S(INFO)<<podlist.getPodTree("test");
|
||||
|
||||
GIVEN("different Attributes for a Track in different forms")
|
||||
{
|
||||
|
||||
WHEN("constructing Track Object with data")
|
||||
{
|
||||
|
||||
THEN("check if Track attributes are correct")
|
||||
{
|
||||
// REQUIRE(podlist.size() == 2);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} //THEN
|
||||
} // WHEN
|
||||
} // GIVEN
|
||||
} //SCENARIO
|
||||
Reference in New Issue
Block a user