SerialConnect: implement XMODEM send.
This commit is contained in:
parent
b32b6a8633
commit
ce058fa0a9
@ -10,6 +10,7 @@ Application SerialConnect :
|
||||
SerialApp.cpp
|
||||
SerialWindow.cpp
|
||||
TermView.cpp
|
||||
XModem.cpp
|
||||
encoding.c
|
||||
input.c
|
||||
parser.c
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
@ -65,6 +65,7 @@ const BPropertyInfo SerialApp::kScriptingProperties(sProperties);
|
||||
SerialApp::SerialApp()
|
||||
: BApplication(SerialApp::kApplicationSignature)
|
||||
, fLogFile(NULL)
|
||||
, fFileSender(NULL)
|
||||
{
|
||||
fWindow = new SerialWindow();
|
||||
|
||||
@ -78,6 +79,7 @@ SerialApp::SerialApp()
|
||||
SerialApp::~SerialApp()
|
||||
{
|
||||
delete fLogFile;
|
||||
delete fFileSender;
|
||||
}
|
||||
|
||||
|
||||
@ -99,21 +101,32 @@ void SerialApp::MessageReceived(BMessage* message)
|
||||
} else {
|
||||
fSerialPort.Close();
|
||||
}
|
||||
|
||||
// Forward to the window so it can enable/disable menu items
|
||||
fWindow->PostMessage(message);
|
||||
return;
|
||||
}
|
||||
case kMsgDataRead:
|
||||
{
|
||||
// forward the message to the window, which will display the
|
||||
// incoming data
|
||||
fWindow->PostMessage(message);
|
||||
const char* bytes;
|
||||
ssize_t length;
|
||||
message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
|
||||
&length);
|
||||
|
||||
if (fLogFile) {
|
||||
const char* bytes;
|
||||
ssize_t length;
|
||||
message->FindData("data", B_RAW_TYPE, (const void**)&bytes,
|
||||
&length);
|
||||
if (fLogFile->Write(bytes, length) != length) {
|
||||
// TODO error handling
|
||||
if (fFileSender != NULL) {
|
||||
if (fFileSender->BytesReceived(bytes, length)) {
|
||||
delete fFileSender;
|
||||
fFileSender = NULL;
|
||||
}
|
||||
} else {
|
||||
// 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:
|
||||
{
|
||||
// Do not allow sending if a file transfer is in progress.
|
||||
if (fFileSender != NULL)
|
||||
return;
|
||||
|
||||
const char* bytes;
|
||||
ssize_t size;
|
||||
|
||||
@ -147,6 +164,25 @@ void SerialApp::MessageReceived(BMessage* message)
|
||||
debugger("Invalid BMessage received");
|
||||
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:
|
||||
{
|
||||
// open the custom baudrate selector window
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
#include <SerialPort.h>
|
||||
#include <String.h>
|
||||
|
||||
#include "XModem.h"
|
||||
|
||||
|
||||
class BFile;
|
||||
class SerialWindow;
|
||||
@ -44,6 +46,8 @@ class SerialApp: public BApplication
|
||||
BFile* fLogFile;
|
||||
BString fPortPath;
|
||||
|
||||
XModemSender* fFileSender;
|
||||
|
||||
static status_t PollSerial(void*);
|
||||
|
||||
static const BPropertyInfo kScriptingProperties;
|
||||
@ -57,7 +61,9 @@ enum messageConstants {
|
||||
kMsgDataWrite = 'dawr',
|
||||
kMsgLogfile = 'logf',
|
||||
kMsgOpenPort = 'open',
|
||||
kMsgProgress = 'prog',
|
||||
kMsgSettings = 'stty',
|
||||
kMsgSendXmodem = 'xmtx',
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include <MenuItem.h>
|
||||
#include <ScrollView.h>
|
||||
#include <SerialPort.h>
|
||||
#include <StatusBar.h>
|
||||
|
||||
#include "SerialApp.h"
|
||||
#include "TermView.h"
|
||||
@ -63,18 +64,27 @@ SerialWindow::SerialWindow()
|
||||
|
||||
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(fTermView);
|
||||
AddChild(scrollBar);
|
||||
AddChild(fStatusBar);
|
||||
|
||||
fConnectionMenu = new BMenu("Connection");
|
||||
BMenu* fileMenu = new BMenu("File");
|
||||
fFileMenu = new BMenu("File");
|
||||
BMenu* settingsMenu = new BMenu("Settings");
|
||||
|
||||
fConnectionMenu->SetRadioMode(true);
|
||||
|
||||
menuBar->AddItem(fConnectionMenu);
|
||||
menuBar->AddItem(fileMenu);
|
||||
menuBar->AddItem(fFileMenu);
|
||||
menuBar->AddItem(settingsMenu);
|
||||
|
||||
// TODO edit menu - what's in it ?
|
||||
@ -83,15 +93,17 @@ SerialWindow::SerialWindow()
|
||||
|
||||
BMenuItem* logFile = new BMenuItem("Log to file" B_UTF8_ELLIPSIS,
|
||||
new BMessage(kMsgLogfile));
|
||||
fileMenu->AddItem(logFile);
|
||||
#if 0
|
||||
fFileMenu->AddItem(logFile);
|
||||
// TODO implement these
|
||||
BMenuItem* xmodemSend = new BMenuItem("X/Y/ZModem send" B_UTF8_ELLIPSIS,
|
||||
NULL);
|
||||
fileMenu->AddItem(xmodemSend);
|
||||
BMenuItem* xmodemSend = new BMenuItem("XModem send" B_UTF8_ELLIPSIS,
|
||||
new BMessage(kMsgSendXmodem));
|
||||
fFileMenu->AddItem(xmodemSend);
|
||||
xmodemSend->SetEnabled(false);
|
||||
#if 0
|
||||
BMenuItem* xmodemReceive = new BMenuItem(
|
||||
"X/Y/Zmodem receive" B_UTF8_ELLIPSIS, NULL);
|
||||
fileMenu->AddItem(xmodemReceive);
|
||||
fFileMenu->AddItem(xmodemReceive);
|
||||
xmodemReceive->SetEnabled(false);
|
||||
#endif
|
||||
|
||||
// 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)
|
||||
{
|
||||
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:
|
||||
{
|
||||
const char* bytes;
|
||||
@ -289,11 +313,13 @@ void SerialWindow::MessageReceived(BMessage* message)
|
||||
return;
|
||||
}
|
||||
case kMsgLogfile:
|
||||
case kMsgSendXmodem:
|
||||
{
|
||||
// Let's lazy init the file panel
|
||||
if (fLogFilePanel == NULL) {
|
||||
fLogFilePanel = new BFilePanel(B_SAVE_PANEL, &be_app_messenger,
|
||||
NULL, B_FILE_NODE, false);
|
||||
fLogFilePanel = new BFilePanel(
|
||||
message->what == kMsgSendXmodem ? B_OPEN_PANEL : B_SAVE_PANEL,
|
||||
&be_app_messenger, NULL, B_FILE_NODE, false);
|
||||
fLogFilePanel->SetMessage(message);
|
||||
}
|
||||
fLogFilePanel->Show();
|
||||
@ -391,6 +417,30 @@ void SerialWindow::MessageReceived(BMessage* message)
|
||||
|
||||
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);
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
class BFilePanel;
|
||||
class BMenu;
|
||||
class BStatusBar;
|
||||
class TermView;
|
||||
|
||||
|
||||
@ -32,7 +33,9 @@ class SerialWindow: public BWindow
|
||||
BMenu* fFlowcontrolMenu;
|
||||
BMenu* fBaudrateMenu;
|
||||
BMenu* fLineTerminatorMenu;
|
||||
BMenu* fFileMenu;
|
||||
BFilePanel* fLogFilePanel;
|
||||
BStatusBar* fStatusBar;
|
||||
|
||||
static const int kBaudrates[];
|
||||
static const int kBaudrateConstants[];
|
||||
|
129
src/apps/serialconnect/XModem.cpp
Normal file
129
src/apps/serialconnect/XModem.cpp
Normal 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;
|
||||
}
|
44
src/apps/serialconnect/XModem.h
Normal file
44
src/apps/serialconnect/XModem.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user