Commit 3d500465 authored by Isabella Skořepová's avatar Isabella Skořepová
Browse files

Performance tweaks (reduced roundtrips)

Moved to vala dbus properties
Port.Controller now calls dbus methods directly
	- Reduces roundtrips to one
Autodetect:
	Always in list command
	If specified port doesn't exist
	 - 1 roundtrip when port exists
	 - 3 if it doesn't or didn't last time
	 - Old method used 2 every time

Bugfix:
Bind now uses port name. Fixes desync issues.
parent ce3a105d
......@@ -58,7 +58,7 @@
# clean: cleans all compilation results for a fresh build
# install: installs this program/library
# uninstall: uninstalls this program/library
#
#
# To install the project, run `make install' with root
# privileges. The default prefix is /usr/local, which can
# be changed by specifying a new PREFIX variable on the
......@@ -88,7 +88,7 @@ TARGET := dserial
# A space-separated list of packages required by your
# project. Names must be compatible with pkg-config, e.g.
# gtk+-3.0
PKGS :=
PKGS :=
# Configure the project's directories.
SRC_DIR := $(DIRECTORY)src/
......@@ -112,7 +112,7 @@ CFLAGS := -O -w -lreadline -lgio-2.0 -g
# Add any additional flags to pass to the compiler during
# the linking phase.
OFLAGS :=
OFLAGS :=
# --------------------------------------------------
# END PROJECT CONFIGURATION
......@@ -143,6 +143,7 @@ BUILDMSG := Generating executable...
# Fast option
ifneq ($(INCREMENTAL), true)
$(BUILD_DIR)$(TARGET): src/*.vala src/commands/*.vala Makefile
@echo ' $(BUILDMSG) ';mkdir -p $(BUILD_DIR) && $(VALAC) $(wildcard src/*.vala) $(wildcard src/*/*.vala) -X -w -o $(BUILD_DIR)$(TARGET) $(VALAFLAGS) -X -lreadline -g
@echo " Done"
......
......@@ -28,7 +28,7 @@ public class BindCommand : Object, Command
}
string? o = Serial.object_name(args[0]);
if(o == null) return error(Err.NONEXISTENT_PORT);
state.bound_port = new Serial.SerialClient(o);
state.bound_port_name = o;
return 0;
}
......
......@@ -24,7 +24,7 @@ public class CloseCommand : Object, Command
public int execute(string[] args, ApplicationState state)
{
Serial.SerialClient p;
if(state.bound_port == null){
if(state.bound_port_name == null){
if(args.length == 0) {
return error(Err.PORT_NOT_SPECIFIED);
}
......@@ -32,7 +32,7 @@ public class CloseCommand : Object, Command
if(o == null) return error(Err.NONEXISTENT_PORT);
p = new Serial.SerialClient(o);
} else {
p=state.bound_port;
p = new Serial.SerialClient(state.bound_port_name);
}
p.close();
return 0;
......@@ -45,7 +45,7 @@ public class CloseCommand : Object, Command
public string[]? args(string[] args, ApplicationState state)
{
if(args.length == 0 && state.bound_port!=null)
if(args.length == 0 && state.bound_port_name != null)
return Serial.get_ports();
else
return {};
......
......@@ -20,7 +20,7 @@
*/
public class ApplicationState: Object {
public Serial.SerialClient? bound_port;
public string? bound_port_name;
public bool running = true;
public Array<Command> command_list;
public bool interactive = false;
......
......@@ -24,7 +24,7 @@ public class GetCommand : Object, Command
public int execute(string[] args, ApplicationState state)
{
Serial.SerialClient p;
if(state.bound_port == null){
if(state.bound_port_name == null){
if(args.length == 0) {
return error(Err.PORT_NOT_SPECIFIED);
}
......@@ -32,14 +32,13 @@ public class GetCommand : Object, Command
if(o == null) return error(Err.NONEXISTENT_PORT);
p = new Serial.SerialClient(o);
} else {
p = state.bound_port;
p.reload_properties();
p = new Serial.SerialClient(state.bound_port_name);
}
if(args.length == (state.bound_port==null?1:0)) {
if(args.length == (state.bound_port_name==null?1:0)) {
return error(Err.SETTING_NOT_SPECIFIED);
}
var s = args[state.bound_port==null?1:0];
var s = args[state.bound_port_name==null?1:0];
if(s=="name") {
stdout.printf("%s\n",p.name);
......@@ -66,9 +65,9 @@ public class GetCommand : Object, Command
public string[]? args(string[] args, ApplicationState state)
{
if(args.length == 0 && state.bound_port == null)
if(args.length == 0 && state.bound_port_name == null)
return Serial.get_ports();
if(args.length == 0 || (args.length == 1 && state.bound_port != null)) {
if(args.length == 0 || (args.length == 1 && state.bound_port_name != null)) {
return {"baudrate","databits","stopbits","parity","flowcontrol",
"autodetected","isopen","name"};
}
......
......@@ -24,7 +24,7 @@ public class InfoCommand : Object, Command
public int execute(string[] args, ApplicationState a)
{
Serial.SerialClient p;
if(a.bound_port == null){
if(a.bound_port_name == null){
if(args.length == 0) {
return error(Err.PORT_NOT_SPECIFIED);
}
......@@ -32,8 +32,7 @@ public class InfoCommand : Object, Command
if(o == null) return error(Err.NONEXISTENT_PORT);
p = new Serial.SerialClient(o);
} else {
p = a.bound_port;
p.reload_properties();
p = new Serial.SerialClient(a.bound_port_name);
}
stdout.printf("name: %s\n",p.name);
......@@ -53,7 +52,7 @@ public class InfoCommand : Object, Command
public string[]? args(string[] args, ApplicationState state)
{
if(args.length == 0 && state.bound_port!=null)
if(args.length == 0 && state.bound_port_name != null)
return Serial.get_ports();
else
return {};
......
......@@ -75,7 +75,7 @@ public class InteractiveCommand : Object, Command
state.interactive = true;
stdout.printf("dserial: Serial communication program\n");
stdout.printf("License: GPL3+\n");
stdout.printf("Version: 0.3\n");
stdout.printf("Version: 0.4\n");
stdout.printf("Write help for help\n");
Readline.History.read(Environment.get_variable("HOME")+"/.dserial-history");
Readline.attempted_completion_function = readline_completion;
......
......@@ -23,6 +23,7 @@ public class ListCommand : Object, Command
{
public int execute(string[] args, ApplicationState state)
{
Serial.autodetect();
string[] ports = Serial.get_ports();
foreach(string port in ports) {
stdout.printf("%s\n",port);
......
......@@ -24,7 +24,7 @@ public class OpenCommand : Object, Command
public int execute(string[] args, ApplicationState a)
{
Serial.SerialClient p;
if(a.bound_port == null){
if(a.bound_port_name == null){
if(args.length == 0) {
return error(Err.PORT_NOT_SPECIFIED);
}
......@@ -32,7 +32,7 @@ public class OpenCommand : Object, Command
if(o == null) return error(Err.NONEXISTENT_PORT);
p = new Serial.SerialClient(o);
} else {
p=a.bound_port;
p = new Serial.SerialClient(a.bound_port_name);
}
p.open();
return 0;
......@@ -45,7 +45,7 @@ public class OpenCommand : Object, Command
public string[]? args(string[] args, ApplicationState state)
{
if(args.length == 0 && state.bound_port!=null)
if(args.length == 0 && state.bound_port_name!=null)
return Serial.get_ports();
else
return {};
......
......@@ -26,7 +26,7 @@ public class ReadCommand : Object, Command
{
loop = new MainLoop();
Serial.SerialClient p;
if(state.bound_port == null){
if(state.bound_port_name == null){
if(other_args.length == 0) {
return error(Err.PORT_NOT_SPECIFIED);
}
......@@ -34,12 +34,11 @@ public class ReadCommand : Object, Command
if(o == null) return error(Err.NONEXISTENT_PORT);
p = new Serial.SerialClient(o);
} else {
p = state.bound_port;
p.reload_properties();
p = new Serial.SerialClient(state.bound_port_name);
}
var display_fields = "Xc";
if(other_args.length >= (state.bound_port==null?2:1)) {
display_fields = other_args[state.bound_port==null?1:0];
if(other_args.length >= (state.bound_port_name==null?2:1)) {
display_fields = other_args[state.bound_port_name==null?1:0];
}
// format string
......@@ -51,8 +50,8 @@ public class ReadCommand : Object, Command
}
int print_length = 0;
if(other_args.length >= (state.bound_port==null?3:2)) {
print_length = int.parse(other_args[state.bound_port==null?2:1]);
if(other_args.length >= (state.bound_port_name==null?3:2)) {
print_length = int.parse(other_args[state.bound_port_name==null?2:1]);
if(print_length <= 0) return error(Err.NEGATIVE_LENGTH);
} else {
int rows;
......@@ -207,7 +206,7 @@ public class ReadCommand : Object, Command
public string[]? args(string[] args, ApplicationState state)
{
if(args.length == 0 && state.bound_port!=null)
if(args.length == 0 && state.bound_port_name!=null)
return Serial.get_ports();
else
return {};
......
......@@ -23,14 +23,14 @@ public class SetCommand : Object, Command
{
public int execute(string[] args, ApplicationState state)
{
if(args.length == (state.bound_port==null?1:0)) {
if(args.length == (state.bound_port_name==null?1:0)) {
return error(Err.SETTING_NOT_SPECIFIED);
} else if(args.length == (state.bound_port==null?2:1)) {
} else if(args.length == (state.bound_port_name==null?2:1)) {
return error(Err.VALUE_NOT_SPECIFIED);
}
Serial.SerialClient p;
if(state.bound_port == null){
if(state.bound_port_name == null){
if(args.length == 0) {
return error(Err.PORT_NOT_SPECIFIED);
}
......@@ -38,11 +38,11 @@ public class SetCommand : Object, Command
if(o == null) return error(Err.NONEXISTENT_PORT);
p = new Serial.SerialClient(o);
} else {
p=state.bound_port;
p = new Serial.SerialClient(state.bound_port_name);
}
var v = args[(state.bound_port==null?2:1)];
var s = args[(state.bound_port==null?1:0)];
var v = args[(state.bound_port_name==null?2:1)];
var s = args[(state.bound_port_name==null?1:0)];
if(s=="name") {
return error(Err.READONLY_SETTING);
} else if(s=="baudrate") {
......@@ -50,12 +50,12 @@ public class SetCommand : Object, Command
if(val == 0) return error(Err.WRONG_VALUE);
p.baudrate = val;
} else if(s=="databits") {
var val = int.parse(v);
uint8 val = (uint8)int.parse(v);
if(val == 0) return error(Err.WRONG_VALUE);
if(!(val in p.available_data_bits)) return error(Err.WRONG_VALUE);
p.data_bits = val;
} else if(s=="stopbits") {
var val = int.parse(v);
var val = (uint8)int.parse(v);
if(val == 0) return error(Err.WRONG_VALUE);
if(!(val in p.available_stop_bits)) return error(Err.WRONG_VALUE);
p.stop_bits = val;
......@@ -98,7 +98,7 @@ public class SetCommand : Object, Command
public string[]? args(string[] args, ApplicationState state)
{
if(state.bound_port == null) {
if(state.bound_port_name == null) {
if(args.length == 0)
return {"baudrate","databits","stopbits","parity","flowcontrol",
"accessmode"};
......
......@@ -23,7 +23,7 @@ public class UnbindCommand : Object, Command
{
public int execute(string[] args, ApplicationState state)
{
state.bound_port = null;
state.bound_port_name = null;
return 0;
}
......@@ -34,7 +34,7 @@ public class UnbindCommand : Object, Command
public string[]? args(string[] args, ApplicationState state)
{
if(state.bound_port == null && args.length == 0)
if(state.bound_port_name == null && args.length == 0)
return Serial.get_ports();
else
return {};
......
......@@ -32,7 +32,7 @@ public class ValuesCommand : Object, Command
OptionValues vals;
if(OptionValues.requires_port(s)) {
Serial.SerialClient p;
if(state.bound_port == null){
if(state.bound_port_name == null){
if(args.length == 1) {
return error(Err.PORT_NOT_SPECIFIED);
}
......@@ -40,7 +40,7 @@ public class ValuesCommand : Object, Command
if(o == null) return error(Err.NONEXISTENT_PORT);
p = new Serial.SerialClient(o);
} else {
p=state.bound_port;
p = new Serial.SerialClient(state.bound_port_name);
}
vals = new OptionValues(s,p);
}else{
......
......@@ -38,7 +38,7 @@ public class WriteCommand : Object, Command
public int execute(string[] args, ApplicationState state)
{
Serial.SerialClient p;
if(state.bound_port == null) {
if(state.bound_port_name == null) {
if(args.length == 0) {
return error(Err.PORT_NOT_SPECIFIED);
}
......@@ -46,15 +46,14 @@ public class WriteCommand : Object, Command
if(o == null) return error(Err.NONEXISTENT_PORT);
p = new Serial.SerialClient(o);
} else {
p = state.bound_port;
p.reload_properties();
p = new Serial.SerialClient(state.bound_port_name);
}
if (args.length == (state.bound_port==null?1:0)) {
if (args.length == (state.bound_port_name==null?1:0)) {
return error(Err.NO_MESSAGE);
}
int start_p = state.bound_port==null?1:0;
int start_p = state.bound_port_name==null?1:0;
// Build buffer
int char_count = -1; // minus 1 for terminating space
......@@ -160,7 +159,7 @@ public class WriteCommand : Object, Command
public string[]? args(string[] args, ApplicationState state)
{
if(state.bound_port == null && args.length == 0) {
if(state.bound_port_name == null && args.length == 0) {
return Serial.get_ports();
}else{
return {};
......
......@@ -20,147 +20,138 @@
*/
namespace Serial {
[DBus (name = "info.skorepa.DSerial1.controller")]
private interface Controller : GLib.Object {
public abstract void autodetect() throws IOError;
public abstract void create_file_port(string file_name) throws IOError;
public abstract void create_fake_port(string file_name) throws IOError;
public abstract string get_path(string file_name) throws IOError;
}
[DBus (name = "org.freedesktop.DBus.Properties")]
private interface Properties : GLib.Object {
public signal void properties_changed(string interf, HashTable<string, Variant> changed_properties_values,
string[] other_changed);
public abstract Variant get(string interface_name, string property_name) throws IOError;
public abstract HashTable<string, Variant> get_all(string interf) throws IOError;
public abstract void set(string interf, string property, Variant value) throws IOError;
}
public static bool DEBUG_OUTPUT = false;
[DBus (name = "info.skorepa.DSerial1.port")]
private interface Methods : GLib.Object {
public abstract void write(uint8[] msg) throws IOError;
public abstract async void write(uint8[] msg) throws IOError;
public abstract void open() throws IOError;
public abstract void close() throws IOError;
public signal void data_recieved(uint8[] msg);
public signal void closing();
public signal void data_sent(uint8[] msg);
public signal void disappeared();
}
/*=====================================
* Return object name for file name
*=====================================*/
public static string? object_name(string file_name) {
autodetect();
try {
Controller c = Bus.get_proxy_sync(BusType.SESSION,"info.skorepa.DSerial1","/controller");
var path = c.get_path(file_name);
if(path == "/") return null;
return path;
} catch (Error e) {
return null;
}
}
/*=====================================
* List all available ports
*=====================================*/
public static string[] get_ports() {
autodetect();
try {
Properties c = Bus.get_proxy_sync(BusType.SESSION,"info.skorepa.DSerial1","/controller");
return c.get("info.skorepa.DSerial1.controller", "PortList").get_strv();
}catch(IOError e){
stdout.printf("Unable to get ports: %s\n",e.message);
}
return new string[0];
public abstract uint8 flow_control {get;set;}
public abstract string name {owned get;}
public abstract int32 baudrate {get;set;}
public abstract uint8 data_bits {get;set;}
public abstract uint8 stop_bits {get;set;}
public abstract uint8 parity {get;set;}
[DBus (name = "Type")]
public abstract uint8 port_type {get;}
}
/*=====================================
* Port autodetection
*=====================================*/
public static void autodetect() {
try {
Controller c = GLib.Bus.get_proxy_sync (BusType.SESSION, "info.skorepa.DSerial1", "/controller");
c.autodetect();
}catch(IOError e){}
if(DEBUG_OUTPUT) stdout.printf("autodetect\n");
try {
GLib.Bus.get_sync(BusType.SESSION).call_sync(
"info.skorepa.DSerial1",
"/controller",
"info.skorepa.DSerial1.controller",
"Autodetect",
null,
null,
DBusCallFlags.NONE,
1000);
}catch(Error e){}
}
/*=====================================
* Port creation
*=====================================*/
public static void create_fake_port(string file_name) {
try {
Controller c = GLib.Bus.get_proxy_sync (BusType.SESSION, "info.skorepa.DSerial1", "/controller");
c.create_fake_port(file_name);
}catch(IOError e){}
try {
GLib.Bus.get_sync(BusType.SESSION).call_sync(
"info.skorepa.DSerial1",
"/controller",
"info.skorepa.DSerial1.controller",
"CreateFakePort",
new Variant("(s)",file_name),
null,
DBusCallFlags.NONE,
1000);
//controller.create_fake_port(file_name);
}catch(Error e){}
}
public static void create_file_port(string file_name) {
try {
Controller c = GLib.Bus.get_proxy_sync (BusType.SESSION, "info.skorepa.DSerial1", "/controller");
c.create_file_port(file_name);
}catch(IOError e){}
try {
GLib.Bus.get_sync(BusType.SESSION).call_sync(
"info.skorepa.DSerial1",
"/controller",
"info.skorepa.DSerial1.controller",
"CreateFilePort",
new Variant("(s)",file_name),
null,
DBusCallFlags.NONE,
1000);
//controller.create_file_port(file_name);
}catch(Error e){}
}
private class PropertiesAccessor : GLib.Object {
Properties properties;
HashTable<string, Variant> cache;
string iface;
public PropertiesAccessor(GLib.BusType t, string _iface, string object) { // info.skorepa.DSerial1.port /info/skorepa/serial/dev/ttyUSB0
iface = _iface;
try {
properties = GLib.Bus.get_proxy_sync<Properties>(t,"info.skorepa.DSerial1",object);
properties.properties_changed.connect(on_property_changed);
} catch (IOError e) {}
reload();
}
public void set_val(string k, Variant v) {
try {
properties.set(iface,k,v);
} catch (IOError e) {}
finally {
cache.replace(k,v);
}
}
public Variant get_val(string k) {
return cache.get(k);
}
private void on_property_changed(string interf, HashTable<string, Variant> changed, string[] other_changed) {
List<string> keys = changed.get_keys();
GLib.Func<string> foreach_f = (k)=>{
var v = changed.get(k);
cache.replace(k,v);
property_changed(k,v);
};
keys.foreach(foreach_f);
}
/*=====================================
* List all available ports
*=====================================*/
public string[] get_ports() {
if(DEBUG_OUTPUT) stdout.printf("get_ports\n");
try {
return GLib.Bus.get_sync(BusType.SESSION).call_sync(
"info.skorepa.DSerial1",
"/controller",
"org.freedesktop.DBus.Properties",
"Get",
new Variant("(ss)","info.skorepa.DSerial1.controller", "PortList"),
null,
DBusCallFlags.NONE,
1000).get_child_value(0).get_variant().get_strv();
}catch(Error e){}
return {};
}
public signal void property_changed(string name, Variant value);
public void reload() {
try {
cache = properties.get_all(iface);
} catch (IOError e) {}
}
/*=====================================
* Return object name for file name
*=====================================*/
public string? object_name(string file_name) {
if(DEBUG_OUTPUT) stdout.printf("object_name\n");
try {
var path = GLib.Bus.get_sync(BusType.SESSION).call_sync(
"info.skorepa.DSerial1",
"/controller",
"info.skorepa.DSerial1.controller",
"GetPath",
new Variant("(s)",file_name),
null,
DBusCallFlags.NONE,
1000).get_child_value(0).get_string();
if(path == "/") { // autodetect and try again
autodetect();
path = GLib.Bus.get_sync(BusType.SESSION).call_sync(
"info.skorepa.DSerial1",
"/controller",
"info.skorepa.DSerial1.controller",
"GetPath",
new Variant("(s)",file_name),
null,
DBusCallFlags.NONE,
1000).get_child_value(0).get_string();
if(path == "/") return null;
};
return path;
} catch (Error e) {
return null;
}