diff --git a/client/client.pro b/client/client.pro
index af2791f..31a9bd7 100644
--- a/client/client.pro
+++ b/client/client.pro
@@ -16,8 +16,6 @@
# along with this program. If not, see .
#
-#CONFIG *= testing
-
# NOTE: You'll occasionally find a file with a capital "C" extension.
# These are C files that we want ignored by the DOS compiler.
@@ -115,7 +113,6 @@ SOURCES = \
src/system/surface.c \
src/system/taglist.c \
$$SHARED/util.c \
- src/test.c \
$$SHARED/array.c \
$$SHARED/log.c \
src/system/timer.c \
@@ -151,10 +148,4 @@ OTHER_FILES = \
../test.conf \
postBuild.sh
-testing {
- DEFINES *= TESTING
- HEADERS += src/test.h
- SOURCES += src/test.c
-}
-
QMAKE_POST_LINK = $$PWD/postBuild.sh "$$PWD" "$$DESTDIR"
diff --git a/client/src/main.c b/client/src/main.c
index 143c1d4..766af19 100644
--- a/client/src/main.c
+++ b/client/src/main.c
@@ -49,25 +49,118 @@
#include "welcome.h"
-#ifdef TESTING
-#include "test.h"
-#endif
-
PacketThreadDataT *__packetThreadData = NULL; // Exported in os.h
+static void taskComDebugLoop(void *data);
static void taskGuiEventLoop(void *data);
+#include "comport.h"
+static void taskComDebugLoop(void *data) {
+ int32_t r;
+ int32_t x;
+ char buffer[1024];
+
+ (void)data;
+
+ // Open COM port.
+ logWrite("Opening COM\n");
+ r = comOpen(__configData.serialCom - 1, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS);
+ if (r != SER_SUCCESS) {
+ logWrite("Unable to open COM port! Please check settings.\n");
+ return;
+ }
+ // Send a CR to clear anything in the modem.
+ logWrite("Clearing modem buffer\n");
+ snprintf(buffer, 1023, "%c", 13);
+ comWrite(__configData.serialCom - 1, buffer, strlen(buffer));
+ // Wait roughly a second for anything.
+ comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4, "weExpectNothing");
+ // Send actual init
+ logWrite("Init modem\n");
+ snprintf(buffer, 1023, "%s%c", "AT+SOCK1", 13);
+ comWrite(__configData.serialCom - 1, buffer, strlen(buffer));
+ // Wait roughly a second for "OK".
+ r = comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4, "\rOK\r");
+ if (r <= 0) {
+ comClose(__configData.serialCom - 1);
+ logWrite("Modem does not support ENET! Please check settings.\n");
+ return;
+ }
+ logWrite("Modem OK\n");
+ // Flush COM port.
+ timerQuarterSecondsWait(4);
+ comReceiveBufferFlush(__configData.serialCom - 1);
+
+ // Show dialing, dial service.
+ logWrite("Dialing\n");
+ snprintf(buffer, 1023, "ATDT%s:%d%c", __configData.serverHost, __configData.serverPort, 13);
+ comWrite(__configData.serialCom - 1, buffer, strlen(buffer));
+ // Wait 7 seconds for welcome banner.
+ r = comWaitWithTimeout(__configData.serialCom - 1, buffer, 1023, 4 * 7, "KPMPGSMKII\r");
+ if (r <= 0) {
+ comClose(__configData.serialCom - 1);
+ logWrite("Unable to connect to server! Please check settings or try later.\n");
+ return;
+ }
+
+ // Connected! Show icon and negotiate session.
+ logWrite("Connected\n");
+
+ PacketEncodeDataT encoded = { 0 };
+ PacketDecodeDataT decoded = { 0 };
+
+ // Send it 10 times, waiting for a PONG between each.
+ for (x=0; x<10; x++) {
+ // Send PING.
+ logWrite("Sending PING %d\n", x);
+ encoded.control = PACKET_CONTROL_DAT;
+ encoded.packetType = PACKET_TYPE_PING;
+ encoded.channel = 1;
+ encoded.encrypt = 0;
+ packetEncode(__packetThreadData, &encoded, NULL, 0); // Must encode each packet - no reusing encoded data.
+ packetSend(__packetThreadData, &encoded);
+ // Wait for PONG.
+ while (1) {
+ r = comRead(__configData.serialCom - 1, buffer, 1);
+ if (r == 1) {
+ if (packetDecode(__packetThreadData, &decoded, buffer, 1)) {
+ if (decoded.packetType == PACKET_TYPE_PONG) {
+ logWrite("Received PONG\n");
+ break;
+ } else {
+ logWrite("Unexpected packet type received %d\n", decoded.packetType);
+ }
+ }
+ }
+ taskYield();
+ }
+ }
+
+ // Send CLIENT_SHUTDOWN.
+ logWrite("Sending CLIENT_SHUTDOWN\n");
+ encoded.control = PACKET_CONTROL_DAT;
+ encoded.packetType = PACKET_TYPE_CLIENT_SHUTDOWN;
+ encoded.channel = 1;
+ encoded.encrypt = 0;
+ packetEncode(__packetThreadData, &encoded, NULL, 0);
+ packetSend(__packetThreadData, &encoded);
+
+ logWrite("Sleeping\n");
+ timerQuarterSecondsWait(8);
+
+ logWrite("COM closed\n");
+ comClose(__configData.serialCom - 1);
+ guiStop();
+}
+
+
static void taskGuiEventLoop(void *data) {
MouseT *mouse = NULL;
ImageT *pointer = NULL;
ColorT alpha;
-#ifdef TESTING
- int8_t debugState = 0;
- int8_t key = 0;
-#endif
(void)data;
@@ -78,34 +171,16 @@ static void taskGuiEventLoop(void *data) {
timerUpdate();
mouse = mouseRead();
if (keyHit()) {
-#ifdef TESTING
- lastKey = keyASCIIGet(); //***DEBUG***
-#endif
guiKeyboardProcess(keyASCIIGet(), keyExtendedGet(), keyScanCodeGet(), keyShiftGet(), keyControlGet(), keyAltGet());
//logWrite("Key '%d' Extended '%d' Scancode '%d' Shift '%d' Control '%d' Alt '%d'\n", keyASCIIGet(), keyExtended(), keyScanCode(), keyShift(), keyControl(), keyAlt());
}
guiMouseProcess(mouse);
guiComposite();
imageRenderWithAlpha(pointer, mouse->x, mouse->y, alpha);
-
-#ifdef TESTING
- if (key == '=') guiDebugWidgetTreeDump(guiRootGet(), 0);
- if (key == '+') {
- debugState++;
- if (debugState > 2) debugState = 0;
- }
- if (debugState > 0) widgetDebugDraw(guiRootGet(), debugState - 1);
- //if (timerHalfSecondOn) guiDrawRectangle(0, 0, vbeSurfaceWidthGet() - 1, vbeSurfaceHeightGet() - 1, vbeMakeColor(255, 255, 255));
-#endif
-
vbeVBlankWait();
vbePresent();
taskYield();
-#ifdef TESTING
- } while (key != 27); // Exit on ESC.
-#else
} while (!guiHasStopped());
-#endif
imageUnload(&pointer);
}
@@ -152,31 +227,15 @@ int main(int argc, char *argv[]) {
guiStartup();
taskStartup();
- __packetThreadData = packetThreadDataCreate();
+ __packetThreadData = packetThreadDataCreate(NULL);
+ packetSenderRegister(comPacketSender);
-#ifdef TESTING
- taskCreate(comPortScanTest, NULL);
-#else
- taskCreate(taskWelcome, NULL);
+ taskCreate(taskComDebugLoop, NULL);
+ //taskCreate(taskWelcome, NULL);
taskCreate(taskGuiEventLoop, NULL);
-#endif
taskRun();
-/*
- char *testPacket = "Hello Server!";
- PacketEncodeDataT encoded;
- encoded.control = PACKET_CONTROL_DAT;
- encoded.packetType = PACKET_TYPE_TEST;
- encoded.channel = 1;
- encoded.encrypt = 0;
- packetEncode(__packetThreadData, &encoded, testPacket, strlen(testPacket));
- logWrite("Encoded %d bytes (raw length %d)\n", encoded.length, strlen(testPacket));
- PacketDecodeDataT decoded;
- packetDecode(__packetThreadData, &decoded, encoded.dataPointer, encoded.length);
- logWrite("Decoded %d bytes (raw length %d)\n", decoded.length, encoded.length);
-*/
-
packetThreadDataDestroy(&__packetThreadData);
taskShutdown();
diff --git a/client/src/system/comport.c b/client/src/system/comport.c
index e76c238..d5dfe2b 100644
--- a/client/src/system/comport.c
+++ b/client/src/system/comport.c
@@ -21,6 +21,14 @@
#include "comport.h"
#include "timer.h"
#include "task.h"
+#include "config.h"
+
+
+void comPacketSender(char *data, uint32_t length, void *userData) {
+ (void)userData;
+
+ comWrite(__configData.serialCom - 1, data, length);
+}
int comReceiveBufferFlush(int com) {
diff --git a/client/src/system/comport.h b/client/src/system/comport.h
index ad25130..d2b3d00 100644
--- a/client/src/system/comport.h
+++ b/client/src/system/comport.h
@@ -23,6 +23,7 @@
#include "os.h"
+#include "packet.h"
#ifdef __linux__
@@ -52,8 +53,9 @@ int comWrite(int com, const char *data, int len);
#endif
-int comReceiveBufferFlush(int com);
-int comWaitWithTimeout(int com, char *buffer, int len, int quarterSeconds, char *expecting);
+void comPacketSender(char *data, uint32_t length, void *userData);
+int comReceiveBufferFlush(int com);
+int comWaitWithTimeout(int com, char *buffer, int len, int quarterSeconds, char *expecting);
#endif // COMPORT_H
diff --git a/client/src/test.c b/client/src/test.c
deleted file mode 100644
index 4280913..0000000
--- a/client/src/test.c
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- * Kangaroo Punch MultiPlayer Game Server Mark II
- * Copyright (C) 2020-2021 Scott Duensing
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- */
-
-#include "test.h"
-
-#include "task.h"
-#include "gui.h"
-#include "widget.h"
-#include "desktop.h"
-#include "window.h"
-#include "button.h"
-#include "label.h"
-#include "checkbox.h"
-#include "radio.h"
-#include "picture.h"
-#include "frame.h"
-#include "textbox.h"
-#include "updown.h"
-#include "listbox.h"
-#include "terminal.h"
-
-#include "taglist.h"
-#include "comport.h"
-
-#include
-
-
-uint8_t _lastKey = 0;
-
-static TerminalT *_t1 = NULL;
-
-
-static void buttonClick(WidgetT *widget);
-//static void test(void *data);
-static void testTerminal(void *data);
-
-
-static void buttonClick(WidgetT *widget) {
- logWrite("'%s' was clicked.\n", ((ButtonT *)widget)->title);
-}
-
-
-void comPortScanTest(void *data) {
- int rc;
- int len;
- char buffer[1024];
-
- (void)data;
-
- for (int x=0; x<4; x++) {
- rc = comOpen(x, 57600L, 8, 'n', 1, SER_HANDSHAKING_RTSCTS);
- if (rc == SER_SUCCESS) {
- logWrite("COM%d exists!\n", x + 1);
- snprintf(buffer, 1024, "%s%c", "AT+SOCK1", 13);
- comWrite(x, buffer, strlen(buffer));
- sleep(1);
- len = comRead(x, buffer, 1024);
- buffer[len] = 0;
- if (strstr(buffer, "OK")) {
- logWrite("ENET SoftModem found!\n");
- } else {
- logWrite("Result: [%s]\n", buffer);
- }
- comClose(x);
- } else {
- logWrite("No COM%d.\n", x + 1);
- }
- }
-
- guiStop();
-}
-
-
-/*
-static void test(void *data) {
- DesktopT *desktop = (DesktopT *)guiRootGet();
- WindowT *w1 = NULL;
- WindowT *w2 = NULL;
- WindowT *w3 = NULL;
- WindowT *w4 = NULL;
- ButtonT *b1 = NULL;
- LabelT *l1 = NULL;
- CheckboxT *c1 = NULL;
- RadioT *r1a = NULL;
- RadioT *r2a = NULL;
- RadioT *r3a = NULL;
- RadioT *r1b = NULL;
- RadioT *r2b = NULL;
- RadioT *r3b = NULL;
- PictureT *p1 = NULL;
- FrameT *f1 = NULL;
- TextboxT *tb1 = NULL;
- TextboxT *tb2 = NULL;
- UpdownT *u1 = NULL;
- ListboxT *lb1 = NULL;
-
- (void)data;
-
- // Windows
- w1 = windowNew(300, 25, 300, 200, "Window 1");
- guiAttach(W(desktop), W(w1));
- w2 = windowNew(150, 150, 300, 200, "Window 2");
- guiAttach(W(desktop), W(w2));
- w3 = windowNew(300, 300, 300, 200, "Window 3");
- guiAttach(W(desktop), W(w3));
- w4 = windowNew(10, 10, 7 + 8 + (80 * 8), 26 + 8 + (24 * 14), "Terminal");
- guiAttach(W(desktop), W(w4));
-
- // Window 1
- p1 = pictureNew(0, 0, "data/kanga.png");
- guiAttach(W(w1), W(p1));
- lb1 = listboxNew(155, 10, 120, 140, "List Box");
- listboxItemAdd(lb1, "One");
- listboxItemAdd(lb1, "Two");
- listboxItemAdd(lb1, "Three");
- listboxItemAdd(lb1, "Four");
- listboxItemAdd(lb1, "Five");
- listboxItemAdd(lb1, "Six");
- listboxItemAdd(lb1, "Seven");
- listboxItemAdd(lb1, "Eight");
- listboxItemAdd(lb1, "Nine");
- listboxItemAdd(lb1, "Ten");
- listboxStepSet(lb1, 3);
- guiAttach(W(w1), W(lb1));
-
- // Window 2
- r1a = radioNew(10, 10, "Radio 1a", 1);
- guiAttach(W(w2), W(r1a));
- r2a = radioNew(20 + widgetWidthGet(W(r1a)), 10, "Radio 2a", 1);
- guiAttach(W(w2), W(r2a));
- r3a = radioNew(30 + widgetWidthGet(W(r1a)) + widgetWidthGet(W(r2a)), 10, "Radio 3a", 1);
- guiAttach(W(w2), W(r3a));
- r1b = radioNew(10, 35, "Radio 1b", 2);
- guiAttach(W(w2), W(r1b));
- r2b = radioNew(20 + widgetWidthGet(W(r1b)), 35, "Radio 2b", 2);
- guiAttach(W(w2), W(r2b));
- r3b = radioNew(30 + widgetWidthGet(W(r1b)) + widgetWidthGet(W(r2b)), 35, "Radio 3b", 2);
- guiAttach(W(w2), W(r3b));
- radioSelectedSet(r1a);
- radioSelectedSet(r2b);
- tb1 = textboxNew(10, 60, 265, "Test Textbox");
- textboxValueSet(tb1, "Really long text string to edit!");
- guiAttach(W(w2), W(tb1));
- tb2 = textboxNew(10, 85, 265, "Test Textbox");
- textboxValueSet(tb2, "Short string.");
- guiAttach(W(w2), W(tb2));
- u1 = updownNew(10, 110, 0, 1024, 5, "UpDown");
- guiAttach(W(w2), W(u1));
-
- // Window 3
- f1 = frameNew(10, 5, 175, 125, "Test Frame");
- guiAttach(W(w3), W(f1));
- b1 = buttonNew(0, 0, "Test Button", buttonClick);
- guiAttach(W(f1), W(b1));
- l1 = labelNew(10, 40, "Test Label");
- guiAttach(W(f1), W(l1));
- c1 = checkboxNew(10, 65, "Test Checkbox");
- guiAttach(W(f1), W(c1));
-
- // Window 4 - Terminal
- t1 = terminalNew(0, 0, 80, 24);
- guiAttach(W(w4), W(t1));
-
- taskCreate(testTerminal, "terminalTest");
-}
-*/
-
-
-void taskTestTagList(void *data) {
- WindowT *w1 = NULL;
- WindowT *w2 = NULL;
- WindowT *w3 = NULL;
- WindowT *w4 = NULL;
- ButtonT *b1 = NULL;
- LabelT *l1 = NULL;
- CheckboxT *c1 = NULL;
- RadioT *r1a = NULL;
- RadioT *r2a = NULL;
- RadioT *r3a = NULL;
- RadioT *r1b = NULL;
- RadioT *r2b = NULL;
- RadioT *r3b = NULL;
- PictureT *p1 = NULL;
- FrameT *f1 = NULL;
- TextboxT *tb1 = NULL;
- TextboxT *tb2 = NULL;
- UpdownT *u1 = NULL;
- ListboxT *lb1 = NULL;
-
- (void)data;
-
- TagItemT ui[] = {
- T_START,
-
- T_WINDOW, O(w1),
- T_TITLE, P("Window 1"),
- T_X, 300, T_Y, 25, T_WIDTH, 300, T_HEIGHT, 200,
- T_PICTURE, O(p1),
- T_X, 0, T_Y, 0,
- T_FILENAME, P("data/kanga.png"),
- T_PICTURE, T_DONE,
- T_LISTBOX, O(lb1),
- T_TITLE, P("Listbox"),
- T_X, 155, T_Y, 10, T_WIDTH, 120, T_HEIGHT, 140,
- T_ITEM, P("One"),
- T_ITEM, P("Two"),
- T_ITEM, P("Three"),
- T_ITEM, P("Four"),
- T_ITEM, P("Five"),
- T_ITEM, P("Six"),
- T_ITEM, P("Seven"),
- T_ITEM, P("Eight"),
- T_ITEM, P("Nine"),
- T_ITEM, P("Ten"),
- T_STEP, 3,
- T_LISTBOX, T_DONE,
- T_WINDOW, T_DONE,
-
- T_WINDOW, O(w2),
- T_TITLE, P("Window 2"),
- T_X, 150, T_Y, 150, T_WIDTH, 300, T_HEIGHT, 200,
- T_RADIOBUTTON, O(r1a),
- T_TITLE, P("Radio 1a"),
- T_X, 10, T_Y, 10,
- T_GROUP, 1,
- T_SELECTED, 1,
- T_RADIOBUTTON, T_DONE,
- T_RADIOBUTTON, O(r2a),
- T_TITLE, P("Radio 2a"),
- T_X, 20 + 80, T_Y, 10,
- T_GROUP, 1,
- T_RADIOBUTTON, T_DONE,
- T_RADIOBUTTON, O(r3a),
- T_TITLE, P("Radio 3a"),
- T_X, 30 + 80 * 2, T_Y, 10,
- T_GROUP, 1,
- T_RADIOBUTTON, T_DONE,
- T_RADIOBUTTON, O(r1b),
- T_TITLE, P("Radio 1b"),
- T_X, 10, T_Y, 35,
- T_GROUP, 2,
- T_RADIOBUTTON, T_DONE,
- T_RADIOBUTTON, O(r2b),
- T_TITLE, P("Radio 2b"),
- T_X, 20 + 80, T_Y, 35,
- T_GROUP, 2,
- T_SELECTED, 1,
- T_RADIOBUTTON, T_DONE,
- T_RADIOBUTTON, O(r3b),
- T_TITLE, P("Radio 3b"),
- T_X, 30 + 80 * 2, T_Y, 35,
- T_GROUP, 2,
- T_RADIOBUTTON, T_DONE,
- T_TEXTBOX, O(tb1),
- T_TITLE, P("Test Textbox"),
- T_X, 10, T_Y, 60, T_WIDTH, 265,
- T_VALUE, P("Really long text string to edit!"),
- T_TEXTBOX, T_DONE,
- T_TEXTBOX, O(tb2),
- T_TITLE, P("Test Textbox"),
- T_X, 10, T_Y, 85, T_WIDTH, 265,
- T_VALUE, P("Short String."),
- T_TEXTBOX, T_DONE,
- T_UPDOWN, O(u1),
- T_TITLE, P("UpDown"),
- T_X, 10, T_Y, 120,
- T_MINIMUM, 0, T_MAXIMUM, 1024, T_STEP, 5,
- T_UPDOWN, T_DONE,
- T_WINDOW, T_DONE,
-
- T_WINDOW, O(w3),
- T_TITLE, P("Window 3"),
- T_X, 300, T_Y, 300, T_WIDTH, 300, T_HEIGHT, 200,
- T_FRAME, O(f1),
- T_TITLE, P("Test Frame"),
- T_X, 10, T_Y, 5, T_WIDTH, 175, T_HEIGHT, 125,
- T_BUTTON, O(b1),
- T_TITLE, P("Test Button"),
- T_X, 0, T_Y, 0,
- T_CLICK, P(buttonClick),
- T_BUTTON, T_DONE,
- T_LABEL, O(l1),
- T_TITLE, P("Test Label"),
- T_X, 10, T_Y, 40,
- T_LABEL, T_DONE,
- T_CHECKBOX, O(c1),
- T_TITLE, P("Test Checkbox"),
- T_X, 10, T_Y, 65,
- T_CHECKBOX, T_DONE,
- T_FRAME, T_DONE,
- T_WINDOW, T_DONE,
-
- T_WINDOW, O(w4),
- T_TITLE, P("Terminal"),
- T_X, 10, T_Y, 10, T_WIDTH, 7 + 8 + (80 * 8), T_HEIGHT, 26 + 8 + (24 * 14),
- T_TERMINAL, O(_t1),
- T_X, 0, T_Y, 0, T_WIDTH, 80, T_HEIGHT, 24,
- T_TERMINAL, T_DONE,
- T_WINDOW, T_DONE,
-
- T_END
- };
-
- tagListRun(ui);
-
- taskCreate(testTerminal, NULL);
-}
-
-
-static void testTerminal(void *data) {
- FILE *in = NULL;
- char *buffer = NULL;
- uint16_t length = 0;
-
- (void)data;
-
- // Load ANSI file for terminal test.
- in = fopen("data/kanga.ans", "rt");
- fseek(in, 0, SEEK_END);
- length = ftell(in);
- fseek(in, 0, SEEK_SET);
- buffer = (char *)malloc(length + 1);
- fread(buffer, 1, length, in);
- fclose(in);
- buffer[length] = 0;
-
- terminalStringPrint(_t1, buffer);
-
- free(buffer);
-}
-
-
-void widgetDebugDraw(WidgetT *widget, uint8_t debugToggle) {
- size_t len = arrlenu(widget->children);
- size_t i;
- RectT r;
-
- if (debugToggle) {
- // Clipping region (blue)
- guiWidgetBoundsDrawableOnScreenGet(widget, &r);
- surfaceRectangleDraw(r.x, r.y, r.x + r.w, r.y + r.h, vbeColorMake(0, 0, 255));
- } else {
- // Widget border (red)
- guiWidgetPositionOnScreenGet(widget, &r);
- surfaceRectangleDraw(r.x, r.y, r.x + widget->pos.w, r.y + widget->pos.h, vbeColorMake(255, 0, 0));
- }
-
- if (len > 0) {
- for (i=0; ichildren[i], debugToggle);
- }
- }
-}
-
diff --git a/client/src/test.h b/client/src/test.h
deleted file mode 100644
index 288e245..0000000
--- a/client/src/test.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Kangaroo Punch MultiPlayer Game Server Mark II
- * Copyright (C) 2020-2021 Scott Duensing
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- */
-
-
-#ifndef TEST_H
-#define TEST_H
-
-
-#include "os.h"
-#include "terminal.h"
-
-
-extern uint8_t _lastKey;
-
-
-void comPortScanTest(void *data);
-void taskTestTagList(void *data);
-void widgetDebugDraw(WidgetT *widget, uint8_t debugToggle);
-
-
-#endif // TEST_H
diff --git a/client/src/welcome.c b/client/src/welcome.c
index ec5a922..6e16f37 100644
--- a/client/src/welcome.c
+++ b/client/src/welcome.c
@@ -132,16 +132,6 @@ static void taskConnectClick(void *data) {
// Connected! Show icon and negotiate session.
widgetVisibleSet(W(_picConnect), 1);
taskYield();
-
- char *testPacket = "Hello Server!";
- PacketEncodeDataT encoded;
- encoded.control = PACKET_CONTROL_DAT;
- encoded.packetType = PACKET_TYPE_TEST;
- encoded.channel = 1;
- encoded.encrypt = 0;
- packetEncode(__packetThreadData, &encoded, testPacket, strlen(testPacket));
- comWrite(__configData.serialCom - 1, encoded.dataPointer, encoded.length);
- logWrite("Sent %d bytes (raw length %d)\n", encoded.length, strlen(testPacket));
}
diff --git a/server/src/client.c b/server/src/client.c
index 546f2df..7397169 100644
--- a/server/src/client.c
+++ b/server/src/client.c
@@ -23,6 +23,7 @@
#include "network.h"
#include "console.h"
#include "packet.h"
+#include "server.h"
static uint8_t clientDequeuePacket(ClientThreadT *client);
@@ -62,13 +63,29 @@ static uint8_t clientDequeuePacket(ClientThreadT *client) {
static void clientProcessPacket(ClientThreadT *client, PacketDecodeDataT *data) {
- char temp[PACKET_MAX + 1] = { 0 };
- uint16_t x = 0;
+ PacketEncodeDataT encoded = { 0 };
- for (x=0; xlength; x++) temp[x] = data->data[x];
- temp[x] = 0;
+ switch (data->packetType) {
+ case PACKET_TYPE_PING:
+ // Build PONG packet.
+ encoded.control = PACKET_CONTROL_DAT;
+ encoded.packetType = PACKET_TYPE_PONG;
+ encoded.channel = 1;
+ encoded.encrypt = 0;
+ packetEncode(client->packetThreadData, &encoded, NULL, 0);
+ // Send it.
+ packetSend(client->packetThreadData, &encoded);
+ logWrite("Got PING, sent PONG\n\r");
+ break;
- consoleMessageQueue("%ld: Channel %d Packet %d [%s]\n", client->threadIndex, data->channel, data->packetType, temp);
+ case PACKET_TYPE_CLIENT_SHUTDOWN:
+ serverDisconnectClient(client);
+ break;
+
+ default:
+ consoleMessageQueue("%ld: Channel %d Unknown Packet %d\n", client->threadIndex, data->channel, data->packetType);
+ break;
+ }
}
@@ -94,14 +111,9 @@ void *clientThread(void *data) {
ENetPeer *peer = (ENetPeer *)data;
ClientThreadT *client = (ClientThreadT *)peer->data;
- ENetPacket *packet = NULL;
struct timespec remaining = { 0, 0 };
struct timespec sleepTime = { 0, 1000000000/100 }; // 1/100th second.
- // Send service banner.
- packet = enet_packet_create("KPMPGSMKII\r", 11, ENET_PACKET_FLAG_RELIABLE);
- enet_peer_send(peer, 0, packet);
-
// Process packets until we're done.
while (client->running) {
if (!clientDequeuePacket(client)) {
diff --git a/server/src/client.h b/server/src/client.h
index e1b6d49..c9cf1a4 100644
--- a/server/src/client.h
+++ b/server/src/client.h
@@ -39,6 +39,7 @@ typedef struct ClientThreadS {
PacketThreadDataT *packetThreadData;
ClientRawPacketT **packetQueue;
pthread_mutex_t packetQueueMutex;
+ void *peer;
} ClientThreadT;
diff --git a/server/src/server.c b/server/src/server.c
index aba44aa..afa6cf9 100644
--- a/server/src/server.c
+++ b/server/src/server.c
@@ -30,6 +30,35 @@ pthread_t _serverThreadHandle = { 0 };
void *_serverThreadStatus = NULL;
+static void serverPacketSender(char *data, uint32_t length, void *userData);
+
+
+void serverDisconnectClient(ClientThreadT *client) {
+ ENetPeer *peer = (ENetPeer *)client->peer;
+ void *status = NULL;
+ char buffer[2048] = { 0 };
+
+ // Stop client processing.
+ client->running = 0;
+ pthread_join(client->threadHandle, &status);
+ // Tell the console.
+ enet_address_get_host_ip(&peer->address, buffer, 2047);
+ consoleMessageQueue("%ld: [%s] disconnected.\n", client->threadIndex, buffer);
+ // Hang up.
+ enet_peer_reset(peer);
+}
+
+
+static void serverPacketSender(char *data, uint32_t length, void *userData) {
+ ENetPeer *peer = (ENetPeer *)userData;
+ ENetPacket *packet = NULL;
+
+ // Send packet.
+ packet = enet_packet_create(data, length, ENET_PACKET_FLAG_RELIABLE);
+ enet_peer_send(peer, 0, packet);
+}
+
+
void serverShutdown(void) {
pthread_join(_serverThreadHandle, &_serverThreadStatus);
enet_host_destroy(_server);
@@ -40,6 +69,9 @@ void serverStartup(uint32_t port, uint32_t maxClients) {
ENetAddress address = { 0 };
pthread_attr_t serverThreadAttributes = { 0 };
+ // Tell the packet code how to send packets.
+ packetSenderRegister(serverPacketSender);
+
// Set up listening socket.
address.host = ENET_HOST_ANY;
address.port = port;
@@ -61,10 +93,10 @@ void serverStartup(uint32_t port, uint32_t maxClients) {
void *serverThread(void *data) {
ENetHost *server = (ENetHost *)data;
ENetEvent event = { 0 };
+ ENetPacket *packet = NULL;
ClientThreadT *client = NULL;
uint64_t nextIndex = 0;
size_t i = 0;
- void *status = NULL;
char buffer[2048] = { 0 };
while (_running) {
@@ -77,7 +109,7 @@ void *serverThread(void *data) {
// Create new client.
NEW(ClientThreadT, client);
if (!client) utilDie("Unable to allocate new client.\n");
- client->packetThreadData = packetThreadDataCreate();
+ client->packetThreadData = packetThreadDataCreate(event.peer);
if (!client->packetThreadData) {
utilDie("Unable to allocate packetThreadData for new client.\n");
break; // This break silences an invalid clang warning.
@@ -85,6 +117,7 @@ void *serverThread(void *data) {
client->threadIndex = nextIndex++;
client->running = 1;
client->packetQueue = NULL;
+ client->peer = event.peer;
memset(&client->packetQueueMutex, 1, sizeof(pthread_mutex_t));
pthread_mutex_init(&client->packetQueueMutex, NULL);
// Keep our client in the peer data for later.
@@ -96,6 +129,9 @@ void *serverThread(void *data) {
// Tell the console.
enet_address_get_host_ip(&event.peer->address, buffer, 2047);
consoleMessageQueue("%ld: [%s] connected.\n", client->threadIndex, buffer);
+ // Send banner to client.
+ packet = enet_packet_create("KPMPGSMKII\r", 11, ENET_PACKET_FLAG_RELIABLE);
+ enet_peer_send(event.peer, 0, packet);
break;
case ENET_EVENT_TYPE_RECEIVE:
@@ -106,14 +142,7 @@ void *serverThread(void *data) {
case ENET_EVENT_TYPE_DISCONNECT:
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT:
- // Get our client.
- client = (ClientThreadT *)event.peer->data;
- // Stop client processing.
- client->running = 0;
- pthread_join(client->threadHandle, &status);
- // Tell the console.
- enet_address_get_host_ip(&event.peer->address, buffer, 2047);
- consoleMessageQueue("%ld: [%s] disconnected.\n", client->threadIndex, buffer);
+ serverDisconnectClient((ClientThreadT *)event.peer->data);
break;
}
}
@@ -123,11 +152,7 @@ void *serverThread(void *data) {
for (i=0; ipeerCount; i++) {
client = (ClientThreadT *)server->peers[i].data;
if (client) {
- // Stop client processing.
- client->running = 0;
- pthread_join(client->threadHandle, &status);
- // Hang up.
- enet_peer_reset(&server->peers[i]);
+ serverDisconnectClient(client);
}
}
diff --git a/server/src/server.h b/server/src/server.h
index a229126..0fb5ab5 100644
--- a/server/src/server.h
+++ b/server/src/server.h
@@ -23,8 +23,10 @@
#include "os.h"
+#include "client.h"
+void serverDisconnectClient(ClientThreadT *client);
void serverShutdown(void);
void serverStartup(uint32_t port, uint32_t maxClients);
void *serverThread(void *data);
diff --git a/shared/packet.c b/shared/packet.c
index 507cbcd..2b35a19 100644
--- a/shared/packet.c
+++ b/shared/packet.c
@@ -21,6 +21,9 @@
#include "packet.h"
+static packetSender _packetSender = NULL;
+
+
static uint8_t packetCRC(char *data, uint16_t length, uint8_t startAt);
@@ -28,7 +31,7 @@ static uint8_t packetCRC(char *data, uint16_t length, uint8_t startAt) {
uint16_t x = 0;
for (x=0; xdecodeBuffer[data->length - 1] != packetCRC(threadData->decodeBuffer, data->length - 1, 0)) continue;
+ // Get sequence value.
+ sequence = ((uint8_t)threadData->decodeBuffer[0]) & 0x1f;
+
+ // Is this a NAK?
+ if ((((uint8_t)threadData->decodeBuffer[0]) & 0xc0) >> 6 == PACKET_CONTROL_NAK) {
+ // Rewind until we find the packet we need in history.
+ x = threadData->historyPosition - 1;
+ if (x < 0) x = PACKET_SEQUENCE_MAX - 1;
+ while (threadData->history[x].sequence != sequence && x != threadData->historyPosition) {
+ x--;
+ if (x < 0) x = PACKET_SEQUENCE_MAX - 1;
+ }
+ // Did we find it?
+ if (x == threadData->historyPosition) {
+ // No! BAD!
+ logWrite("Unable to locate missing packet in history!\n\r");
+ } else {
+ // Yes. Replay missing packets.
+ while (x != threadData->historyPosition) {
+ logWrite("Resending %d!\n\r", threadData->history[x].sequence);
+ _packetSender(threadData->history[x].data, threadData->history[x].length, threadData->senderData);
+ x++;
+ if (x >= PACKET_SEQUENCE_MAX) x = 0;
+ }
+ }
+ continue;
+ }
+
+ // Is this the sequence number we're expecting?
+ if (sequence == threadData->lastRemoteSequence) {
+ // Yes!
+ threadData->lastRemoteSequence++;
+ } else {
+ // No! NAK it!
+ logWrite("Packet out of sequence! Got %d wanted %d!\n\r", sequence, threadData->lastRemoteSequence);
+ nak.control = PACKET_CONTROL_NAK; // Negative acknowledge.
+ nak.packetType = PACKET_TYPE_NONE; // Not destined for the app.
+ nak.channel = 0; // Channel doesn't matter for NAK.
+ nak.encrypt = 0; // Encryption doesn't matter for NAK.
+ nak.sequence = threadData->lastRemoteSequence; // The last good packet we saw.
+ packetEncode(threadData, &nak, NULL, 0);
+ packetSend(threadData, &nak);
+ continue;
+ }
+
// Fill decoded data fields.
data->packetType = threadData->decodeBuffer[1];
data->channel = threadData->decodeBuffer[2];
@@ -88,10 +138,15 @@ uint8_t packetDecode(PacketThreadDataT *threadData, PacketDecodeDataT *data, cha
}
- // Copy packet data to new buffer.
- data->data = (char *)malloc(data->length - 4); // 4 for 3 byte header and 1 byte CRC.
- if (!data) continue;
- memcpy(data->data, &threadData->decodeBuffer[3], data->length - 4); // Skip header and CRC.
+ // Copy packet data to new buffer, if any.
+ if (data->length - 4 > 0) {
+ data->data = (char *)malloc(data->length - 4); // 4 for 3 byte header and 1 byte CRC.
+ if (!data) continue;
+ memcpy(data->data, &threadData->decodeBuffer[3], data->length - 4); // Skip header and CRC.
+ } else {
+ // No payload.
+ data->data = NULL;
+ }
// Fix length to remove header and checksum.
data->length -= 4;
break;
@@ -138,11 +193,14 @@ uint8_t packetEncode(PacketThreadDataT *threadData, PacketEncodeDataT *data, cha
if (length > PACKET_MAX) return 0;
data->dataPointer = NULL;
- data->length = 0;
- data->sequence = threadData->sequence++;
+ data->length = 0;
+
+ if (data->control == PACKET_CONTROL_DAT) {
+ data->sequence = threadData->sequence++;
+ }
// Make needed header bytes.
- control = (data->control << 6) + (data->encrypt << 5) + (data->sequence & 0x1f);
+ control = (((uint8_t)data->control) << 6) + (data->encrypt << 5) + (data->sequence & 0x1f);
// Calculate CRC over header bytes and payload.
crc = packetCRC((char *)&control, 1, crc);
@@ -185,15 +243,47 @@ uint8_t packetEncode(PacketThreadDataT *threadData, PacketEncodeDataT *data, cha
}
-PacketThreadDataT *packetThreadDataCreate(void) {
+void packetSend(PacketThreadDataT *threadData, PacketEncodeDataT *data) {
+ // Valid control type?
+ if (data->control != PACKET_CONTROL_BAD && data->control <= PACKET_CONTROL_COUNT) {
+ _packetSender(data->dataPointer, data->length, threadData->senderData);
+ } else {
+ logWrite("Invalid PACKET_CONTROL!\n\r");
+ }
+
+ // Add to history?
+ if (data->control == PACKET_CONTROL_DAT) {
+ threadData->history[threadData->historyPosition].sequence = data->sequence;
+ threadData->history[threadData->historyPosition].length = data->length;
+ memcpy(threadData->history[threadData->historyPosition].data, data->dataPointer, data->length);
+ threadData->historyPosition++;
+ if (threadData->historyPosition >= PACKET_SEQUENCE_MAX) {
+ threadData->historyPosition = 0;
+ }
+ }
+
+ // Mark invalid so caller has to change it.
+ data->control = PACKET_CONTROL_BAD;
+}
+
+
+void packetSenderRegister(packetSender sender) {
+ _packetSender = sender;
+}
+
+
+PacketThreadDataT *packetThreadDataCreate(void *senderData) {
PacketThreadDataT *data = NULL;
data = (PacketThreadDataT *)malloc(sizeof(PacketThreadDataT));
if (data) {
- data->sequence = 0;
- data->decodeQueueHead = 0;
- data->decodeQueueTail = 0;
- data->newPacket = 1;
+ data->sequence = 0;
+ data->lastRemoteSequence = 0;
+ data->historyPosition = 0;
+ data->decodeQueueHead = 0;
+ data->decodeQueueTail = 0;
+ data->newPacket = 1;
+ data->senderData = senderData;
}
return data;
diff --git a/shared/packet.h b/shared/packet.h
index 53789a8..4c9ae7b 100644
--- a/shared/packet.h
+++ b/shared/packet.h
@@ -39,60 +39,87 @@
*/
-#define PACKET_MAX 1024 // Maximum number of bytes per packet
-#define PACKET_SEQUENCE_MAX 32 // Five bits of data
+#define PACKET_MAX 1024 // Maximum number of bytes per packet
+#define PACKET_SEQUENCE_MAX 16 // Five bits of data, 32 max.
#define PACKET_INPUT_QUEUE_SIZE 4096
#define PACKET_BUFFER_SIZE (PACKET_MAX * 2 + 2 + 8) // Worst case, every byte is a PACKET_FRAME. Add two for ending frame. Add 8 for worst case header and CRC.
-#define PACKET_FRAME 0x7e
+#define PACKET_FRAME 0x7e
-#define PACKET_CONTROL_DAT 0 // Regular data packet.
-#define PACKET_CONTROL_RTX 1 // Retransmission packet.
-#define PACKET_CONTROL_NAK 2 // Negative acknowledge.
-#define PACKET_TYPE_NONE 0 // No packet.
-#define PACKET_TYPE_TEST 1
+// This enum must be 4 entries or less.
+typedef enum PacketControlE {
+ PACKET_CONTROL_DAT = 0, // Regular data packet.
+ PACKET_CONTROL_RTX, // Retransmission packet.
+ PACKET_CONTROL_NAK, // Negative acknowledge.
+ PACKET_CONTROL_BAD,
+ PACKET_CONTROL_COUNT
+} PacketControlT;
+
+// This enum is treated as BYTES in the code. Do not go over 255 entries.
+typedef enum PacketTypeE {
+ PACKET_TYPE_NONE = 0, // No packet.
+ PACKET_TYPE_PING,
+ PACKET_TYPE_PONG,
+ PACKET_TYPE_SERVER_SHUTDOWN,
+ PACKET_TYPE_CLIENT_SHUTDOWN,
+ PACKET_TYPE_COUNT
+} PacketTypeT;
typedef struct PacketDecodeDataS {
// Output
- uint8_t packetType; // One of PACKET_TYPE_*
- char *data; // Buffer received. MUST FREE.
- uint16_t length; // Length of buffer.
- uint8_t channel; // Which data channel it arrived on.
+ PacketTypeT packetType; // One of PACKET_TYPE_*
+ char *data; // Buffer received. MUST FREE.
+ uint16_t length; // Length of buffer.
+ uint8_t channel; // Which data channel it arrived on.
} PacketDecodeDataT;
typedef struct PacketEncodeDataS {
// Input
- uint8_t control; // One of PACKET_CONTROL_*
- uint8_t packetType; // One of PACKET_TYPE_*
- uint8_t channel; // Which data channel to use.
- uint8_t encrypt; // Do we want the packet encrypted?
+ PacketControlT control; // One of PACKET_CONTROL_*
+ PacketTypeT packetType; // One of PACKET_TYPE_*
+ uint8_t channel; // Which data channel to use.
+ uint8_t encrypt; // Do we want the packet encrypted?
// Input & Output
- uint16_t sequence; // For NAK packets, input the sequence number we missed. Other packets returns the sequence of this packet.
+ uint8_t sequence; // For NAK packets, input the sequence number we missed. Other packets returns the sequence of this packet.
// Output
- char *dataPointer; // Buffer ready to send. DO NOT FREE.
- uint16_t length; // Length of buffer.
+ char *dataPointer; // Buffer ready to send. DO NOT FREE.
+ uint16_t length; // Length of buffer.
} PacketEncodeDataT;
+typedef struct PacketHistoryDataS {
+ char data[PACKET_BUFFER_SIZE];
+ uint16_t length;
+ uint8_t sequence;
+} PacketHistoryDataT;
+
typedef struct PacketThreadDataS {
// Internal state per thread for packet processing.
- uint8_t sequence;
- char decodeBuffer[PACKET_MAX];
- char encodeBuffer[PACKET_BUFFER_SIZE];
- char decodeQueue[PACKET_INPUT_QUEUE_SIZE];
- uint16_t decodeQueueHead;
- uint16_t decodeQueueTail;
- uint8_t inEscape;
- uint8_t newPacket;
+ uint8_t sequence;
+ uint8_t lastRemoteSequence;
+ PacketHistoryDataT history[PACKET_SEQUENCE_MAX];
+ uint8_t historyPosition;
+ char decodeBuffer[PACKET_MAX];
+ char encodeBuffer[PACKET_BUFFER_SIZE];
+ char decodeQueue[PACKET_INPUT_QUEUE_SIZE];
+ uint16_t decodeQueueHead;
+ uint16_t decodeQueueTail;
+ uint8_t inEscape;
+ uint8_t newPacket;
+ void *senderData;
} PacketThreadDataT;
+typedef void (*packetSender)(char *data, uint32_t length, void *userData);
+
uint8_t packetDecode(PacketThreadDataT *threadData, PacketDecodeDataT *data, char *input, uint16_t length);
void packetDecodeDataDestroy(PacketDecodeDataT **packet);
uint8_t packetEncode(PacketThreadDataT *threadData, PacketEncodeDataT *data, char *input, uint16_t length);
-PacketThreadDataT *packetThreadDataCreate(void);
+void packetSend(PacketThreadDataT *threadData, PacketEncodeDataT *data);
+void packetSenderRegister(packetSender sender);
+PacketThreadDataT *packetThreadDataCreate(void *senderData);
void packetThreadDataDestroy(PacketThreadDataT **data);
diff --git a/shared/util.c b/shared/util.c
index f7852c4..d313d1d 100644
--- a/shared/util.c
+++ b/shared/util.c
@@ -51,16 +51,12 @@ char *utilAppNameWithNewExtensionGet(char *appName, char *extension) {
}
-void utilDie(const char *why, ...) {
- va_list args;
- char msg[2048];
+void utilBitsPrint(uint8_t byte) {
+ int i = 0;
- va_start(args, why);
- vsprintf(msg, why, args);
- va_end(args);
-
- logWrite("DIE: %s", msg);
- exit(1);
+ for (i = 7; 0 <= i; i--) {
+ printf("%c", (byte & (1 << i)) ? '1' : '0');
+ }
}
@@ -93,3 +89,15 @@ char *utilCreateStringVArgs(char *format, va_list args) {
return buffer;
}
+
+void utilDie(const char *why, ...) {
+ va_list args;
+ char msg[2048];
+
+ va_start(args, why);
+ vsprintf(msg, why, args);
+ va_end(args);
+
+ logWrite("DIE: %s", msg);
+ exit(1);
+}
diff --git a/shared/util.h b/shared/util.h
index 25e7dbe..e302373 100644
--- a/shared/util.h
+++ b/shared/util.h
@@ -26,9 +26,10 @@
char *utilAppNameWithNewExtensionGet(char *appName, char *extension);
-void utilDie(const char *why, ...);
+void utilBitsPrint(uint8_t byte);
char *utilCreateString(char *format, ...);
char *utilCreateStringVArgs(char *format, va_list args);
+void utilDie(const char *why, ...);
#endif // UTIL_H