SerialConnect: implement XMODEM send.

This commit is contained in:
Adrien Destugues 2017-07-16 10:21:22 +02:00
parent b32b6a8633
commit ce058fa0a9
7 changed files with 293 additions and 24 deletions

View File

@ -10,6 +10,7 @@ Application SerialConnect :
SerialApp.cpp SerialApp.cpp
SerialWindow.cpp SerialWindow.cpp
TermView.cpp TermView.cpp
XModem.cpp
encoding.c encoding.c
input.c input.c
parser.c parser.c

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012, Adrien Destugues, pulkomandy@gmail.com * Copyright 2012-2017, Adrien Destugues, pulkomandy@gmail.com
* Distributed under the terms of the MIT licence. * Distributed under the terms of the MIT licence.
*/ */
@ -65,6 +65,7 @@ const BPropertyInfo SerialApp::kScriptingProperties(sProperties);
SerialApp::SerialApp() SerialApp::SerialApp()
: BApplication(SerialApp::kApplicationSignature) : BApplication(SerialApp::kApplicationSignature)
, fLogFile(NULL) , fLogFile(NULL)
, fFileSender(NULL)
{ {
fWindow = new SerialWindow(); fWindow = new SerialWindow();
@ -78,6 +79,7 @@ SerialApp::SerialApp()
SerialApp::~SerialApp() SerialApp::~SerialApp()
{ {
delete fLogFile; delete fLogFile;
delete fFileSender;
} }
@ -99,21 +101,32 @@ void SerialApp::MessageReceived(BMessage* message)
} else { } else {
fSerialPort.Close(); fSerialPort.Close();
} }
// Forward to the window so it can enable/disable menu items
fWindow->PostMessage(message);
return; return;
} }
case kMsgDataRead: case kMsgDataRead:
{ {
// forward the message to the window, which will display the const char* bytes;
// incoming data ssize_t length;
fWindow->PostMessage(message); message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
&length);
if (fLogFile) { if (fFileSender != NULL) {
const char* bytes; if (fFileSender->BytesReceived(bytes, length)) {
ssize_t length; delete fFileSender;
message->FindData("data", B_RAW_TYPE, (const void**)&bytes, fFileSender = NULL;
&length); }
if (fLogFile->Write(bytes, length) != length) { } else {
// TODO error handling // forward the message to the window, which will display the
// incoming data
fWindow->PostMessage(message);
if (fLogFile) {
if (fLogFile->Write(bytes, length) != length) {
// TODO error handling
}
} }
} }
@ -121,6 +134,10 @@ void SerialApp::MessageReceived(BMessage* message)
} }
case kMsgDataWrite: case kMsgDataWrite:
{ {
// Do not allow sending if a file transfer is in progress.
if (fFileSender != NULL)
return;
const char* bytes; const char* bytes;
ssize_t size; ssize_t size;
@ -147,6 +164,25 @@ void SerialApp::MessageReceived(BMessage* message)
debugger("Invalid BMessage received"); debugger("Invalid BMessage received");
return; return;
} }
case kMsgSendXmodem:
{
entry_ref ref;
if (message->FindRef("refs", &ref) == B_OK) {
BFile* file = new BFile(&ref, B_READ_ONLY);
status_t error = file->InitCheck();
if (error != B_OK)
puts(strerror(error));
else {
delete fFileSender;
fFileSender = new XModemSender(file, &fSerialPort, fWindow);
}
} else {
message->PrintToStream();
debugger("Invalid BMessage received");
}
return;
}
case kMsgCustomBaudrate: case kMsgCustomBaudrate:
{ {
// open the custom baudrate selector window // open the custom baudrate selector window

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012, Adrien Destugues, pulkomandy@gmail.com * Copyright 2012-2017, Adrien Destugues, pulkomandy@gmail.com
* Distributed under the terms of the MIT licence. * Distributed under the terms of the MIT licence.
*/ */
@ -13,6 +13,8 @@
#include <SerialPort.h> #include <SerialPort.h>
#include <String.h> #include <String.h>
#include "XModem.h"
class BFile; class BFile;
class SerialWindow; class SerialWindow;
@ -44,6 +46,8 @@ class SerialApp: public BApplication
BFile* fLogFile; BFile* fLogFile;
BString fPortPath; BString fPortPath;
XModemSender* fFileSender;
static status_t PollSerial(void*); static status_t PollSerial(void*);
static const BPropertyInfo kScriptingProperties; static const BPropertyInfo kScriptingProperties;
@ -57,7 +61,9 @@ enum messageConstants {
kMsgDataWrite = 'dawr', kMsgDataWrite = 'dawr',
kMsgLogfile = 'logf', kMsgLogfile = 'logf',
kMsgOpenPort = 'open', kMsgOpenPort = 'open',
kMsgProgress = 'prog',
kMsgSettings = 'stty', kMsgSettings = 'stty',
kMsgSendXmodem = 'xmtx',
}; };
#endif #endif

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2015, Adrien Destugues, pulkomandy@pulkomandy.tk * Copyright 2012-2017, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under the terms of the MIT licence. * Distributed under the terms of the MIT licence.
*/ */
@ -15,6 +15,7 @@
#include <MenuItem.h> #include <MenuItem.h>
#include <ScrollView.h> #include <ScrollView.h>
#include <SerialPort.h> #include <SerialPort.h>
#include <StatusBar.h>
#include "SerialApp.h" #include "SerialApp.h"
#include "TermView.h" #include "TermView.h"
@ -63,18 +64,27 @@ SerialWindow::SerialWindow()
ResizeTo(r.right - 1, r.bottom + B_H_SCROLL_BAR_HEIGHT - 1); ResizeTo(r.right - 1, r.bottom + B_H_SCROLL_BAR_HEIGHT - 1);
r = fTermView->Frame();
r.top = r.bottom - 37;
fStatusBar = new BStatusBar(r, "file transfer progress", NULL, NULL);
fStatusBar->SetResizingMode(B_FOLLOW_BOTTOM | B_FOLLOW_LEFT_RIGHT);
fStatusBar->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
fStatusBar->Hide();
AddChild(menuBar); AddChild(menuBar);
AddChild(fTermView); AddChild(fTermView);
AddChild(scrollBar); AddChild(scrollBar);
AddChild(fStatusBar);
fConnectionMenu = new BMenu("Connection"); fConnectionMenu = new BMenu("Connection");
BMenu* fileMenu = new BMenu("File"); fFileMenu = new BMenu("File");
BMenu* settingsMenu = new BMenu("Settings"); BMenu* settingsMenu = new BMenu("Settings");
fConnectionMenu->SetRadioMode(true); fConnectionMenu->SetRadioMode(true);
menuBar->AddItem(fConnectionMenu); menuBar->AddItem(fConnectionMenu);
menuBar->AddItem(fileMenu); menuBar->AddItem(fFileMenu);
menuBar->AddItem(settingsMenu); menuBar->AddItem(settingsMenu);
// TODO edit menu - what's in it ? // TODO edit menu - what's in it ?
@ -83,15 +93,17 @@ SerialWindow::SerialWindow()
BMenuItem* logFile = new BMenuItem("Log to file" B_UTF8_ELLIPSIS, BMenuItem* logFile = new BMenuItem("Log to file" B_UTF8_ELLIPSIS,
new BMessage(kMsgLogfile)); new BMessage(kMsgLogfile));
fileMenu->AddItem(logFile); fFileMenu->AddItem(logFile);
#if 0
// TODO implement these // TODO implement these
BMenuItem* xmodemSend = new BMenuItem("X/Y/ZModem send" B_UTF8_ELLIPSIS, BMenuItem* xmodemSend = new BMenuItem("XModem send" B_UTF8_ELLIPSIS,
NULL); new BMessage(kMsgSendXmodem));
fileMenu->AddItem(xmodemSend); fFileMenu->AddItem(xmodemSend);
xmodemSend->SetEnabled(false);
#if 0
BMenuItem* xmodemReceive = new BMenuItem( BMenuItem* xmodemReceive = new BMenuItem(
"X/Y/Zmodem receive" B_UTF8_ELLIPSIS, NULL); "X/Y/Zmodem receive" B_UTF8_ELLIPSIS, NULL);
fileMenu->AddItem(xmodemReceive); fFileMenu->AddItem(xmodemReceive);
xmodemReceive->SetEnabled(false);
#endif #endif
// Configuring all this by menus may be a bit unhandy. Make a setting // Configuring all this by menus may be a bit unhandy. Make a setting
@ -279,6 +291,18 @@ void SerialWindow::MessageReceived(BMessage* message)
{ {
switch (message->what) switch (message->what)
{ {
case kMsgOpenPort:
{
BString path;
bool open = (message->FindString("port name", &path) == B_OK);
int i = 1; // Skip "log to file", which woeks even when offline.
BMenuItem* item;
while((item = fFileMenu->ItemAt(i++)))
{
item->SetEnabled(open);
}
return;
}
case kMsgDataRead: case kMsgDataRead:
{ {
const char* bytes; const char* bytes;
@ -289,11 +313,13 @@ void SerialWindow::MessageReceived(BMessage* message)
return; return;
} }
case kMsgLogfile: case kMsgLogfile:
case kMsgSendXmodem:
{ {
// Let's lazy init the file panel // Let's lazy init the file panel
if (fLogFilePanel == NULL) { if (fLogFilePanel == NULL) {
fLogFilePanel = new BFilePanel(B_SAVE_PANEL, &be_app_messenger, fLogFilePanel = new BFilePanel(
NULL, B_FILE_NODE, false); message->what == kMsgSendXmodem ? B_OPEN_PANEL : B_SAVE_PANEL,
&be_app_messenger, NULL, B_FILE_NODE, false);
fLogFilePanel->SetMessage(message); fLogFilePanel->SetMessage(message);
} }
fLogFilePanel->Show(); fLogFilePanel->Show();
@ -391,6 +417,30 @@ void SerialWindow::MessageReceived(BMessage* message)
return; return;
} }
case kMsgProgress:
{
// File transfer progress
int32 pos = message->FindInt32("pos");
int32 size = message->FindInt32("size");
BString label = message->FindString("info");
if (pos >= size) {
if (!fStatusBar->IsHidden()) {
fStatusBar->Hide();
fTermView->ResizeBy(0, fStatusBar->Bounds().Height() - 1);
}
} else {
BString text;
text.SetToFormat("%" B_PRId32 "/%" B_PRId32, pos, size);
fStatusBar->SetMaxValue(size);
fStatusBar->SetTo(pos, label, text);
if (fStatusBar->IsHidden()) {
fStatusBar->Show();
fTermView->ResizeBy(0, -(fStatusBar->Bounds().Height() - 1));
}
}
return;
}
} }
BWindow::MessageReceived(message); BWindow::MessageReceived(message);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2015, Adrien Destugues, pulkomandy@pulkomandy.tk * Copyright 2012-2017, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under the terms of the MIT licence. * Distributed under the terms of the MIT licence.
*/ */
@ -9,6 +9,7 @@
class BFilePanel; class BFilePanel;
class BMenu; class BMenu;
class BStatusBar;
class TermView; class TermView;
@ -32,7 +33,9 @@ class SerialWindow: public BWindow
BMenu* fFlowcontrolMenu; BMenu* fFlowcontrolMenu;
BMenu* fBaudrateMenu; BMenu* fBaudrateMenu;
BMenu* fLineTerminatorMenu; BMenu* fLineTerminatorMenu;
BMenu* fFileMenu;
BFilePanel* fLogFilePanel; BFilePanel* fLogFilePanel;
BStatusBar* fStatusBar;
static const int kBaudrates[]; static const int kBaudrates[];
static const int kBaudrateConstants[]; static const int kBaudrateConstants[];

View File

@ -0,0 +1,129 @@
/*
* Copyright 2017, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under terms of the MIT license.
*/
#include "XModem.h"
#include "SerialApp.h"
#include <String.h>
#include <stdio.h>
#include <string.h>
// ASCII control characters used in XMODEM protocol
static const char kSOH = 1;
static const char kEOT = 4;
static const char kACK = 6;
static const char kNAK = 21;
static const char kSUB = 26;
static const int kBlockSize = 128;
XModemSender::XModemSender(BDataIO* source, BSerialPort* sink, BHandler* listener)
: fSource(source),
fSink(sink),
fListener(listener),
fBlockNumber(0),
fEotSent(false)
{
fStatus = "Waiting for receiver" B_UTF8_ELLIPSIS;
BPositionIO* pos = dynamic_cast<BPositionIO*>(source);
if (pos)
pos->GetSize(&fSourceSize);
else
fSourceSize = 0;
NextBlock();
}
XModemSender::~XModemSender()
{
delete fSource;
}
bool
XModemSender::BytesReceived(const char* data, size_t length)
{
size_t i;
for (i = 0; i < length; i++)
{
switch (data[i])
{
case kNAK:
if (fEotSent) {
fSink->Write(&kEOT, 1);
} else {
fStatus = "Checksum error, re-send block";
SendBlock();
}
break;
case kACK:
if (fEotSent) {
return true;
}
if (NextBlock() == B_OK) {
fStatus = "Sending" B_UTF8_ELLIPSIS;
SendBlock();
} else {
fStatus = "Everything sent, waiting for acknowledge";
fSink->Write(&kEOT, 1);
fEotSent = true;
}
break;
default:
break;
}
}
return false;
}
void
XModemSender::SendBlock()
{
uint8_t header[3];
uint8_t checksum = 0;
int i;
header[0] = kSOH;
header[1] = fBlockNumber;
header[2] = 255 - fBlockNumber;
for (i = 0; i < kBlockSize; i++)
checksum += fBuffer[i];
fSink->Write(header, 3);
fSink->Write(fBuffer, kBlockSize);
fSink->Write(&checksum, 1);
}
status_t
XModemSender::NextBlock()
{
memset(fBuffer, kSUB, kBlockSize);
if (fSource->Read(fBuffer, kBlockSize) > 0) {
fBlockNumber++;
BMessage msg(kMsgProgress);
msg.AddInt32("pos", fBlockNumber);
msg.AddInt32("size", fSourceSize / kBlockSize);
msg.AddString("info", fStatus);
fListener.SendMessage(&msg);
return B_OK;
}
return B_ERROR;
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2017, Adrien Destugues, pulkomandy@pulkomandy.tk
* Distributed under terms of the MIT license.
*/
#ifndef XMODEM_H
#define XMODEM_H
#include <Messenger.h>
#include <String.h>
class BDataIO;
class BHandler;
class BSerialPort;
class XModemSender {
public:
XModemSender(BDataIO* source, BSerialPort* sink,
BHandler* listener);
~XModemSender();
bool BytesReceived(const char* data, size_t length);
private:
void SendBlock();
status_t NextBlock();
private:
BDataIO* fSource;
BSerialPort* fSink;
BMessenger fListener;
off_t fBlockNumber;
off_t fSourceSize;
uint8_t fBuffer[128];
bool fEotSent;
BString fStatus;
};
#endif /* !XMODEM_H */