Skip to content

Commit e14a9fd

Browse files
committed
Add MockAmpServer
1 parent 7401d92 commit e14a9fd

15 files changed

+3077
-0
lines changed

CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
1919
# =============================================================================
2020
option(EGIAMP_BUILD_GUI "Build the GUI application" ON)
2121
option(EGIAMP_BUILD_CLI "Build the CLI application" ON)
22+
option(EGIAMP_BUILD_MOCK "Build the mock EGI Amp device for testing" ON)
2223

2324
# LSL dependency options
2425
set(LSL_SOURCE_DIR "" CACHE PATH "Path to liblsl source for parallel development (highest priority)")
@@ -158,6 +159,11 @@ if(EGIAMP_BUILD_GUI)
158159
add_subdirectory(src/gui)
159160
endif()
160161

162+
# Mock EGI Amp device for testing
163+
if(EGIAMP_BUILD_MOCK)
164+
add_subdirectory(mock)
165+
endif()
166+
161167
# =============================================================================
162168
# Installation
163169
# =============================================================================

mock/AmpServerAPI.json

Lines changed: 515 additions & 0 deletions
Large diffs are not rendered by default.

mock/CMakeLists.txt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
cmake_minimum_required(VERSION 3.14)
2+
project(MockAmpServer VERSION 1.0.0 LANGUAGES CXX)
3+
4+
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
7+
# Find dependencies
8+
find_package(Threads REQUIRED)
9+
10+
# Include FetchContent for asio
11+
include(FetchContent)
12+
FetchContent_Declare(
13+
asio
14+
GIT_REPOSITORY https://github.com/chriskohlhoff/asio.git
15+
GIT_TAG asio-1-28-0
16+
)
17+
FetchContent_MakeAvailable(asio)
18+
19+
# Create the mock server executable
20+
add_executable(MockAmpServer
21+
src/main.cpp
22+
src/MockAmplifier.cpp
23+
src/CommandHandler.cpp
24+
src/NotificationHandler.cpp
25+
src/DataStreamGenerator.cpp
26+
src/ECIHandler.cpp
27+
)
28+
29+
target_include_directories(MockAmpServer PRIVATE
30+
${CMAKE_CURRENT_SOURCE_DIR}/include
31+
${asio_SOURCE_DIR}/asio/include
32+
)
33+
34+
target_compile_definitions(MockAmpServer PRIVATE
35+
ASIO_STANDALONE
36+
ASIO_NO_DEPRECATED
37+
)
38+
39+
target_link_libraries(MockAmpServer PRIVATE Threads::Threads)
40+
41+
# Platform-specific
42+
if(APPLE)
43+
target_link_libraries(MockAmpServer PRIVATE "-framework CoreFoundation")
44+
endif()
45+
46+
# Install
47+
install(TARGETS MockAmpServer RUNTIME DESTINATION bin)

mock/include/AmpServerProtocol.h

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#ifndef MOCK_AMPSERVERPROTOCOL_H
2+
#define MOCK_AMPSERVERPROTOCOL_H
3+
4+
#include <cstdint>
5+
#include <string>
6+
7+
namespace mock {
8+
9+
// Network ports
10+
constexpr uint16_t COMMAND_PORT = 9877;
11+
constexpr uint16_t NOTIFICATION_PORT = 9878;
12+
constexpr uint16_t DATA_PORT = 9879;
13+
constexpr uint16_t ECI_PORT = 55513;
14+
15+
// Packet format sizes
16+
constexpr size_t PACKET_FORMAT1_SIZE = 1152;
17+
constexpr size_t PACKET_FORMAT2_SIZE = 1264;
18+
constexpr size_t DATA_HEADER_SIZE = 16;
19+
20+
// Amplifier types
21+
enum class AmplifierType : int {
22+
NA300 = 0,
23+
NA400 = 1,
24+
NA410 = 2,
25+
NA500 = 3,
26+
GTEN200 = 4,
27+
Unknown = 5
28+
};
29+
30+
// Net codes
31+
enum class NetCode : uint8_t {
32+
GSN64_2_0 = 0,
33+
GSN128_2_0 = 1,
34+
GSN256_2_0 = 2,
35+
HCGSN32_1_0 = 3,
36+
HCGSN64_1_0 = 4,
37+
HCGSN128_1_0 = 5,
38+
HCGSN256_1_0 = 6,
39+
MCGSN32_1_0 = 7,
40+
MCGSN64_1_0 = 8,
41+
MCGSN128_1_0 = 9,
42+
MCGSN256_1_0 = 10,
43+
TestConnector = 14,
44+
NoNet = 15,
45+
Unknown = 0xFF
46+
};
47+
48+
// Packet format types
49+
enum class PacketFormat : int {
50+
Format1 = 1,
51+
Format2 = 2
52+
};
53+
54+
// Waveform shapes
55+
enum class WaveShape : int {
56+
Sine = 0,
57+
Square = 1,
58+
Triangle = 2,
59+
Sawtooth = 3
60+
};
61+
62+
#pragma pack(push, 1)
63+
64+
// PIB Aux structure for Packet Format 2
65+
struct PacketFormat2_PIB_AUX {
66+
uint8_t digitalInputs;
67+
uint8_t status;
68+
uint8_t batteryLevel[3];
69+
uint8_t temperature[3];
70+
uint8_t sp02;
71+
uint8_t heartRate[2];
72+
};
73+
74+
// Packet Format 2 structure
75+
struct PacketFormat2_SamplePacket {
76+
uint16_t digitalInputs;
77+
uint8_t tr;
78+
PacketFormat2_PIB_AUX pib1_aux;
79+
PacketFormat2_PIB_AUX pib2_aux;
80+
uint64_t packetCounter;
81+
uint64_t timeStamp;
82+
uint8_t netCode;
83+
uint8_t reserved[38];
84+
int32_t eegData[256];
85+
int32_t auxData[3];
86+
int32_t refMonitor;
87+
int32_t comMonitor;
88+
int32_t driveMonitor;
89+
int32_t diagnosticsChannel;
90+
int32_t currentSense;
91+
int32_t pib1_Data[16];
92+
int32_t pib2_Data[16];
93+
};
94+
95+
// Packet Format 1 structure
96+
struct PacketFormat1_SamplePacket {
97+
uint32_t header[8];
98+
float eeg[256];
99+
float pib[7];
100+
float unused1;
101+
float ref;
102+
float com;
103+
float unused2;
104+
float padding[13];
105+
};
106+
107+
// Data stream header
108+
struct AmpDataPacketHeader {
109+
int64_t ampID;
110+
uint64_t length;
111+
};
112+
113+
#pragma pack(pop)
114+
115+
// Utility functions
116+
inline int getChannelCountFromNetCode(NetCode code) {
117+
switch (code) {
118+
case NetCode::HCGSN32_1_0:
119+
case NetCode::MCGSN32_1_0:
120+
return 32;
121+
case NetCode::GSN64_2_0:
122+
case NetCode::HCGSN64_1_0:
123+
case NetCode::MCGSN64_1_0:
124+
return 64;
125+
case NetCode::GSN128_2_0:
126+
case NetCode::HCGSN128_1_0:
127+
case NetCode::MCGSN128_1_0:
128+
return 128;
129+
case NetCode::GSN256_2_0:
130+
case NetCode::HCGSN256_1_0:
131+
case NetCode::MCGSN256_1_0:
132+
case NetCode::TestConnector:
133+
return 256;
134+
default:
135+
return 0;
136+
}
137+
}
138+
139+
inline float getScalingFactor(AmplifierType type) {
140+
switch (type) {
141+
case AmplifierType::NA300:
142+
return 0.0244140625f;
143+
case AmplifierType::NA400:
144+
case AmplifierType::GTEN200:
145+
return 0.000155220429f;
146+
case AmplifierType::NA410:
147+
return 0.00009636188f;
148+
default:
149+
return 1.0f;
150+
}
151+
}
152+
153+
inline const char* amplifierTypeName(AmplifierType type) {
154+
switch (type) {
155+
case AmplifierType::NA300:
156+
return "NA300";
157+
case AmplifierType::NA400:
158+
return "NA400";
159+
case AmplifierType::NA410:
160+
return "NA410";
161+
case AmplifierType::NA500:
162+
return "NA500";
163+
default:
164+
return "Unknown";
165+
}
166+
}
167+
168+
inline const char* netCodeName(NetCode code) {
169+
switch (code) {
170+
case NetCode::GSN64_2_0: return "GSN64_2_0";
171+
case NetCode::GSN128_2_0: return "GSN128_2_0";
172+
case NetCode::GSN256_2_0: return "GSN256_2_0";
173+
case NetCode::HCGSN32_1_0: return "HCGSN32_1_0";
174+
case NetCode::HCGSN64_1_0: return "HCGSN64_1_0";
175+
case NetCode::HCGSN128_1_0: return "HCGSN128_1_0";
176+
case NetCode::HCGSN256_1_0: return "HCGSN256_1_0";
177+
case NetCode::MCGSN32_1_0: return "MCGSN32_1_0";
178+
case NetCode::MCGSN64_1_0: return "MCGSN64_1_0";
179+
case NetCode::MCGSN128_1_0: return "MCGSN128_1_0";
180+
case NetCode::MCGSN256_1_0: return "MCGSN256_1_0";
181+
case NetCode::TestConnector: return "TestConnector";
182+
case NetCode::NoNet: return "NoNet";
183+
default: return "Unknown";
184+
}
185+
}
186+
187+
} // namespace mock
188+
189+
#endif // MOCK_AMPSERVERPROTOCOL_H

mock/include/CommandHandler.h

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#ifndef MOCK_COMMANDHANDLER_H
2+
#define MOCK_COMMANDHANDLER_H
3+
4+
#include "MockAmplifier.h"
5+
#include <asio.hpp>
6+
#include <functional>
7+
#include <memory>
8+
#include <string>
9+
#include <unordered_map>
10+
#include <vector>
11+
12+
namespace mock {
13+
14+
class NotificationHandler;
15+
16+
using NotificationCallback = std::function<void(const std::string&)>;
17+
18+
class CommandHandler : public std::enable_shared_from_this<CommandHandler> {
19+
public:
20+
CommandHandler(asio::io_context& io_context, uint16_t port,
21+
std::shared_ptr<MockAmplifier> amplifier,
22+
std::shared_ptr<NotificationHandler> notificationHandler);
23+
~CommandHandler();
24+
25+
void start();
26+
void stop();
27+
28+
private:
29+
void acceptConnections();
30+
void handleConnection(std::shared_ptr<asio::ip::tcp::socket> socket);
31+
void readCommand(std::shared_ptr<asio::ip::tcp::socket> socket);
32+
33+
std::string processCommand(const std::string& command);
34+
std::string parseAndExecuteCommand(const std::string& cmdLine);
35+
36+
// Command implementations
37+
std::string cmdNone(int64_t ampId, int16_t channel, const std::string& value);
38+
std::string cmdStart(int64_t ampId, int16_t channel, const std::string& value);
39+
std::string cmdStop(int64_t ampId, int16_t channel, const std::string& value);
40+
std::string cmdSetPower(int64_t ampId, int16_t channel, const std::string& value);
41+
std::string cmdReset(int64_t ampId, int16_t channel, const std::string& value);
42+
43+
std::string cmdTurnAll10KOhms(int64_t ampId, int16_t channel, const std::string& value);
44+
std::string cmdTurnChannel10KOhms(int64_t ampId, int16_t channel, const std::string& value);
45+
std::string cmdSetCOM10KOhms(int64_t ampId, int16_t channel, const std::string& value);
46+
std::string cmdSetReference10KOhms(int64_t ampId, int16_t channel, const std::string& value);
47+
48+
std::string cmdTurnAllDriveSignals(int64_t ampId, int16_t channel, const std::string& value);
49+
std::string cmdTurnChannelDriveSignals(int64_t ampId, int16_t channel, const std::string& value);
50+
std::string cmdSetCOMDriveSignal(int64_t ampId, int16_t channel, const std::string& value);
51+
std::string cmdSetReferenceDriveSignal(int64_t ampId, int16_t channel, const std::string& value);
52+
53+
std::string cmdSetSubjectGround(int64_t ampId, int16_t channel, const std::string& value);
54+
std::string cmdSetCurrentSource(int64_t ampId, int16_t channel, const std::string& value);
55+
std::string cmdSetCalibrationSignalFreq(int64_t ampId, int16_t channel, const std::string& value);
56+
std::string cmdSetBufferedReference(int64_t ampId, int16_t channel, const std::string& value);
57+
std::string cmdSetOscillatorGate(int64_t ampId, int16_t channel, const std::string& value);
58+
std::string cmdSetWaveShape(int64_t ampId, int16_t channel, const std::string& value);
59+
std::string cmdSetDrivenCommon(int64_t ampId, int16_t channel, const std::string& value);
60+
std::string cmdSetCalibrationSignalAmplitude(int64_t ampId, int16_t channel, const std::string& value);
61+
62+
std::string cmdSetDigitalOutputData(int64_t ampId, int16_t channel, const std::string& value);
63+
std::string cmdSetDigitalInOutDirection(int64_t ampId, int16_t channel, const std::string& value);
64+
65+
std::string cmdSetNativeRate(int64_t ampId, int16_t channel, const std::string& value);
66+
std::string cmdSetDecimatedRate(int64_t ampId, int16_t channel, const std::string& value);
67+
68+
std::string cmdGetStartTime(int64_t ampId, int16_t channel, const std::string& value);
69+
std::string cmdGetAmpDetails(int64_t ampId, int16_t channel, const std::string& value);
70+
std::string cmdGetAmpStatus(int64_t ampId, int16_t channel, const std::string& value);
71+
72+
std::string cmdTurnChannelZeroOhms(int64_t ampId, int16_t channel, const std::string& value);
73+
std::string cmdTurnAllZeroOhms(int64_t ampId, int16_t channel, const std::string& value);
74+
75+
std::string cmdSetPIBChannelGain(int64_t ampId, int16_t channel, const std::string& value);
76+
std::string cmdGetPhysioConnectionStatus(int64_t ampId, int16_t channel, const std::string& value);
77+
78+
std::string cmdNumberOfAmps(int64_t ampId, int16_t channel, const std::string& value);
79+
std::string cmdNumberOfActiveAmps(int64_t ampId, int16_t channel, const std::string& value);
80+
std::string cmdListenToAmp(int64_t ampId, int16_t channel, const std::string& value);
81+
std::string cmdStopListeningToAmp(int64_t ampId, int16_t channel, const std::string& value);
82+
std::string cmdReceiveNotifications(int64_t ampId, int16_t channel, const std::string& value);
83+
std::string cmdStopReceivingNotifications(int64_t ampId, int16_t channel, const std::string& value);
84+
std::string cmdExit(int64_t ampId, int16_t channel, const std::string& value);
85+
86+
std::string cmdDefaultAcquisitionState(int64_t ampId, int16_t channel, const std::string& value);
87+
std::string cmdDefaultSignalGeneration(int64_t ampId, int16_t channel, const std::string& value);
88+
89+
// GTEN commands
90+
std::string cmdGTENSetTrain(int64_t ampId, int16_t channel, const std::string& value);
91+
std::string cmdGTENSetWaveforms(int64_t ampId, int16_t channel, const std::string& value);
92+
std::string cmdGTENSetBlocks(int64_t ampId, int16_t channel, const std::string& value);
93+
std::string cmdGTENStartTrain(int64_t ampId, int16_t channel, const std::string& value);
94+
std::string cmdGTENAbortTrain(int64_t ampId, int16_t channel, const std::string& value);
95+
std::string cmdGTENGetStatus(int64_t ampId, int16_t channel, const std::string& value);
96+
std::string cmdGTENResetAlarm(int64_t ampId, int16_t channel, const std::string& value);
97+
98+
// Helper
99+
std::string successResponse();
100+
std::string errorResponse();
101+
std::string responseWithData(const std::string& data);
102+
103+
void sendNotification(const std::string& notification);
104+
105+
asio::io_context& io_context_;
106+
asio::ip::tcp::acceptor acceptor_;
107+
std::shared_ptr<MockAmplifier> amplifier_;
108+
std::shared_ptr<NotificationHandler> notificationHandler_;
109+
std::atomic<bool> running_{false};
110+
std::atomic<bool> exitRequested_{false};
111+
int nextClientId_ = 0;
112+
113+
using CommandFunc = std::function<std::string(CommandHandler*, int64_t, int16_t, const std::string&)>;
114+
std::unordered_map<std::string, CommandFunc> commandMap_;
115+
116+
void initCommandMap();
117+
};
118+
119+
} // namespace mock
120+
121+
#endif // MOCK_COMMANDHANDLER_H

0 commit comments

Comments
 (0)