Moved json to include. Wrote complete state class. Main.cpp now contains the TCP socket server logic.

This commit is contained in:
mmcwilliams 2024-04-19 19:48:14 -06:00
parent 0afeaab695
commit b438e44aff
12 changed files with 355 additions and 81 deletions

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "src/json"]
path = src/json
path = include/json
url = https://github.com/nlohmann/json.git

View File

@ -6,25 +6,28 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules)
set(TESTING_ON FALSE)
find_package(OpenGL REQUIRED)
find_package(GLUT REQUIRED)
find_package(OpenCV HINTS /usr/local/opt/opencv /usr/local/Cellar/opencv REQUIRED)
set( NAME_SRC
src/main.cpp
)
file(GLOB SOURCES "src/*.cpp")
if(${TESTING_ON})
ENABLE_TESTING()
ADD_SUBDIRECTORY( test )
set(UNIT_TEST state_test)
SET(UNIT_TEST state_test)
add_test(NAME ${UNIT_TEST} COMMAND ${UNIT_TEST})
add_custom_target(run_unit_test ALL
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
DEPENDS ${UNIT_TEST})
endif()
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
add_executable( fd ${NAME_SRC} ${NAME_HEADERS} )
add_executable( fd ${SOURCES} )
target_include_directories(fd PRIVATE include)
if (APPLE)
# Equivalent to pass flags -framework OpenGL

View File

@ -24,7 +24,8 @@ class Image {
uint16_t height;
Mat blank;
public:
Image(uint16_t w, uint16_t h);
Image();
void setDimensions(uint16_t w, uint16_t h);
Mat getBlank();
};

61
include/state.hpp Normal file
View File

@ -0,0 +1,61 @@
#ifndef STATE_h
#define STATE_h
#include "json/single_include/nlohmann/json.hpp"
#include <iostream>
#include <sys/stat.h>
using json = nlohmann::json;
using namespace std;
enum Action {
NONE,
LOAD,
DISPLAY,
STOP
};
enum Mode {
RGB,
BW,
INVERT,
BW_INVERT,
RGB_CHANNELS,
INVERT_CHANNELS
};
class State {
private:
Action action = STOP;
Mode mode = RGB;
string image;
vector<uint64_t> exposure = {};
uint64_t x;
uint64_t y;
uint64_t width;
uint64_t height;
bool ERROR = false;
void error();
bool imageExists(string& image);
void setAction(json& msgData);
void setImage(json& msgData);
void setMode(json& msgData);
void setExposure(json& msgData);
void setPosition(json& msgData);
public :
State();
void receiveMessage(string msgString);
string createMessage(bool success);
Action getAction () { return action; }
Mode getMode() { return mode; }
string getImage () { return image; }
vector<uint64_t> getExposure() { return exposure; }
vector<uint64_t> getPosition() { return { x, y, width, height }; }
bool isError () { return ERROR; }
};
#endif

5
src/image.cpp Normal file
View File

@ -0,0 +1,5 @@
#include "image.hpp"
Image::Image () {
//
}

View File

@ -1,10 +1,141 @@
#include "image.h"
#include "state.h"
#include "image.hpp"
#include "state.hpp"
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <string>
#include <arpa/inet.h>
#include <unistd.h>
#include <mutex>
#include <thread>
#include <condition_variable>
using namespace std;
int main(int argc, char** argv) {
string action_load = "{\"action\" : 0 }";
json msg_data = json::parse(action_load);
MessageIn msgIn(msg_data);
uint16_t PORT = 8081;
int BUFFER_SIZE = 1024;
Image img;
State state;
bool receivedMessage = false;
bool sendMessage = false;
string incomingMessage;
string outgoingMessage;
mutex receivedMutex;
condition_variable receivedCondVar;
mutex sendMutex;
condition_variable sendCondVar;
void performAction () {
if (receivedMessage) {
cout << "RECEIVED" << endl;
state.receiveMessage(incomingMessage);
receivedMessage = false;
if (state.isError()) {
outgoingMessage = state.createMessage(false);
} else {
outgoingMessage = state.createMessage(true);
}
sendMessage = true;
}
}
void handleTCPConnection(int clientSocket) {
char buffer[BUFFER_SIZE];
memset(buffer, 0, BUFFER_SIZE);
while (true) {
int bytesRead = recv(clientSocket, buffer, BUFFER_SIZE - 1, 0);
if (bytesRead <= 0) {
// Client disconnected or error occurred
break;
}
std::string iMessage(buffer, bytesRead);
{
lock_guard<mutex> lock(receivedMutex);
receivedMessage = true;
incomingMessage = iMessage;
receivedCondVar.notify_all();
}
performAction();
bool localSendMessage;
string localOutgoingMessage;
{
unique_lock<mutex> lock(sendMutex);
sendCondVar.wait(lock, [&] { return sendMessage; });
localSendMessage = sendMessage;
localOutgoingMessage = outgoingMessage;
}
if (localSendMessage) {
const char* msg = localOutgoingMessage.c_str();
int bytesSent = send(clientSocket, msg, strlen(msg), 0);
if (bytesSent < 0) {
std::cerr << "Error sending ACK to client" << std::endl;
break;
}
}
memset(buffer, 0, BUFFER_SIZE);
}
}
void runTCPServer (int serverSocket) {
while (true) {
struct sockaddr_in clientAddr;
socklen_t clientAddrLen = sizeof(clientAddr);
int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
if (clientSocket < 0) {
std::cerr << "Error accepting client connection" << std::endl;
continue;
}
cout << "New client connected" << endl;
handleTCPConnection(clientSocket);
}
}
int main (int argc, char** argv) {
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0) {
cerr << "Error creating socket" << endl;
return 1;
}
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(PORT);
int bindResult = ::bind(serverSocket, reinterpret_cast<struct sockaddr*>(&serverAddr), sizeof(serverAddr));
if (bindResult < 0) {
std::cerr << "Error binding socket" << std::endl;
close(serverSocket);
return 1;
}
if (listen(serverSocket, 5) < 0) {
cerr << "Error listening on socket" << endl;
return 1;
}
cout << "TCP server listening on port " << PORT << endl;
thread serverThread(runTCPServer, serverSocket);
serverThread.join();
close(serverSocket);
return 0;
}

View File

@ -1,7 +1,92 @@
#include "state.h"
#include "state.hpp"
MessageIn::MessageIn (json msgData) {
if (msgData.contains("action")) {
State::State () {
//
}
void State::error () {
ERROR = true;
image.clear();
}
bool State::imageExists (string& image) {
struct stat buffer;
return (stat (image.c_str(), &buffer) == 0);
}
void State::setAction (json& msgData) {
action = static_cast<Action>(msgData["action"]);
cout << "Action = " << action << endl;
}
void State::setImage (json& msgData) {
image = msgData["image"];
if (!imageExists(image)) {
error();
cerr << "Image " << msgData["image"] << " does not exist" << endl;
return;
}
cout << "Image = " << image << endl;
}
void State::setMode (json& msgData) {
mode = static_cast<Mode>(msgData["mode"]);
cout << "Mode = " << mode << endl;
}
void State::setPosition (json& msgData) {
x = msgData["position"]["x"];
y = msgData["position"]["y"];
width = msgData["position"]["w"];
height = msgData["position"]["h"];
cout << "Position[x] = " << x << " [y] = " << y << " [width] = " << width << " [height] = " << height << endl;
}
void State::setExposure (json& msgData) {
if (msgData["exposure"].size() == 1) {
exposure = { msgData["exposure"][0] };
} else if (msgData["exposure"].size() == 3) {
exposure = { msgData["exposure"][0], msgData["exposure"][1], msgData["exposure"][2] };
} else {
error();
cerr << "Exposure array is incorrect length, " << msgData["exposure"].size() << endl;
return;
}
}
void State::receiveMessage (string msgString) {
ERROR = false;
json msgData = json::parse(msgString);
if (msgData.contains("action")) {
setAction(msgData);
} else {
action = NONE;
error();
cerr << "Received invalid message: " << msgString << endl;
return;
}
if (msgData.contains("image")) {
setImage(msgData);
}
if (action == LOAD && msgData.contains("mode")) {
setMode(msgData);
}
if (action == LOAD && msgData.contains("position")) {
setPosition(msgData);
}
if (action == DISPLAY && msgData.contains("exposure")) {
setExposure(msgData);
}
}
string State::createMessage (bool success) {
json msgData = {
{ "action", action },
{ "success", success }
};
return msgData.dump();
}

View File

@ -1,56 +0,0 @@
#ifndef STATE_h
#define STATE_h
#include "json/single_include/nlohmann/json.hpp"
#include <iostream>
using json = nlohmann::json;
using namespace std;
enum Action {
LOAD,
DISPLAY,
STOP
};
enum Mode {
RGB,
BW,
INVERT,
BW_INVERT,
RGB_CHANNELS,
INVERT_CHANNELS
};
class MessageIn {
private:
Action action;
string file;
Mode mode;
vector<uint64_t> exposure;
bool start;
public:
MessageIn(json msgJson);
Action getAction () { return action;}
};
/*
class MessageOut {
public:
MessageOut(Action a, bool s);
Action action;
bool success;
string toString();
};
class State {
public :
MessageIn active;
State();
void processMessage(string msgString);
void createMessage(Action action, bool success);
};
*/
#endif

View File

@ -1,2 +1,2 @@
ADD_EXECUTABLE( state_test state_test.cpp )
ADD_EXECUTABLE( state_test state_test.cpp ${SOURCES} )
ADD_TEST( fd state_test )

View File

@ -1,5 +1,5 @@
#include <iostream>
#include "../src/state.h"
#include "state.hpp"
void IS_TRUE(string name, bool x) {
if (!(x)) {
@ -10,6 +10,7 @@ void IS_TRUE(string name, bool x) {
}
void test_message_in() {
State state;
//string action_load = "{\"action\" : 0 }";
//json msg_data = json::parse(action_load);
//MessageIn msgIn(msg_data);

43
test/test_messages.js Normal file
View File

@ -0,0 +1,43 @@
const net = require('net');
const serverAddress = 'localhost';
const serverPort = 8081;
const client = new net.Socket();
console.log(`Connecting to ${serverAddress}:${serverPort}...`);
async function delay (ms) {
return new Promise((resolve, reject) => {
return setTimeout(resolve, ms);
});
}
client.connect(serverPort, serverAddress, async () => {
const data = {
action : 1,
image: 'filename.tif',
};
const jsonData = JSON.stringify(data);
console.log('SENDING');
console.log(jsonData);
client.write(jsonData);
await delay(2000);
jsonData.action = 2;
console.log('SENDING');
console.log(jsonData);
client.write(jsonData);
});
client.on('data', (data) => {
console.log('RECEIVED');
console.log(data.toString());
});
client.on('close', () => {
console.log('Closing connection');
});
client.on('error', (err) => {
console.error('Error:', err);
});