-package "märklin"
+package "r2c2"
{
require "mspcore";
require "sigc++-2.0";
build_info
{
incpath "source";
- library "marklin";
+ library "r2c2";
};
require "mspdatafile";
};
- library "marklin"
+ library "r2c2"
{
- source "source/libmarklin";
+ source "source/libr2c2";
require "mspdatafile";
install true;
};
- library "marklin3d"
+ library "r2c2_3d"
{
source "source/3d";
require "mspgl";
build_info
{
incpath "source";
- library "marklin";
+ library "r2c2";
};
install true;
};
- library "marklinnet"
+ library "r2c2_net"
{
source "source/network";
require "mspnet";
build_info
{
incpath "source";
- library "marklin";
+ library "r2c2";
};
install true;
};
build_info
{
incpath "source";
- library "marklin3d";
+ library "r2c2_3d";
};
};
build_info
{
incpath "source";
- library "marklin3d";
- library "marklinnet";
+ library "r2c2_3d";
+ library "r2c2_net";
};
};
build_info
{
incpath "source";
- library "marklinnet";
+ library "r2c2_net";
};
};
build_info
{
incpath "source";
- library "marklinnet";
+ library "r2c2_net";
};
};
};
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
+++ /dev/null
-/* $Id$ */
-
-default_font "dejavu-12.font";
-
-style "root"
-{
- special "children";
-};
-
-style "label"
-{
- font_color 0 0 0;
- special "text";
-};
-
-style "image"
-{
- special "image"
- {
- fill 1.0 1.0;
- };
-};
-
-graphic "tooltip"
-{
- texture "gui.png";
- slice 50 93 20 10;
- border { top 2; right 2; bottom 2; left 2; };
-};
-
-style "label-tooltip"
-{
- font_color 0.0 0.0 0.0;
-
- part
- {
- graphic NORMAL "tooltip";
- };
-
- special "text"
- {
- align 0.0 0.5;
- fill 0.0 0.0;
- margin { top 2; right 2; bottom 2; left 2; };
- };
-};
-
-graphic "grey_button"
-{
- texture "gui.png";
- slice 2 54 21 10;
- border { top 2; right 3; bottom 4; left 2; };
- shadow { top 0; right 1; bottom 2; left 0; };
-};
-
-graphic "grey_button_pressed"
-{
- texture "gui.png";
- slice 25 54 21 10;
- border { top 2; right 3; bottom 4; left 2; };
- shadow { top 0; right 1; bottom 2; left 0; };
-};
-
-graphic "green_button"
-{
- texture "gui.png";
- slice 2 43 21 10;
- border { top 2; right 3; bottom 4; left 2; };
- shadow { top 0; right 1; bottom 2; left 0; };
-};
-
-graphic "green_button_pressed"
-{
- texture "gui.png";
- slice 25 43 21 10;
- border { top 2; right 3; bottom 4; left 2; };
- shadow { top 0; right 1; bottom 2; left 0; };
-};
-
-graphic "red_button"
-{
- texture "gui.png";
- slice 2 32 21 10;
- border { top 2; right 3; bottom 4; left 2; };
- shadow { top 0; right 1; bottom 2; left 0; };
-};
-
-graphic "red_button_pressed"
-{
- texture "gui.png";
- slice 25 32 21 10;
- border { top 2; right 3; bottom 4; left 2; };
- shadow { top 0; right 1; bottom 2; left 0; };
-};
-
-graphic "up_arrow_button"
-{
- texture "gui.png";
- slice 2 18 13 13;
- shadow { top 0; right 1; bottom 2; left 0; };
-};
-
-graphic "up_arrow_button_pressed"
-{
- texture "gui.png";
- slice 17 18 13 13;
- shadow { top 0; right 1; bottom 2; left 0; };
-};
-
-graphic "down_arrow_button"
-{
- texture "gui.png";
- slice 2 4 13 13;
- shadow { top 0; right 1; bottom 2; left 0; };
-};
-
-graphic "down_arrow_button_pressed"
-{
- texture "gui.png";
- slice 17 4 13 13;
- shadow { top 0; right 1; bottom 2; left 0; };
-};
-
-style "button"
-{
- font_color 0 0 0;
-
- part
- {
- graphic NORMAL "grey_button";
- graphic ACTIVE "grey_button_pressed";
- align 0.5 0.0;
- fill 0.0 0.0;
- };
-
- special "text"
- {
- align 0.5 1.0;
- fill 0.0 0.0;
- };
-};
-
-style "button-green"
-{
- font_color 0 0 0;
-
- part
- {
- graphic NORMAL "green_button";
- graphic ACTIVE "green_button_pressed";
- align 0.5 0.0;
- fill 0.0 0.0;
- };
-
- special "text"
- {
- align 0.5 1.0;
- fill 0.0 0.0;
- };
-};
-
-style "button-red"
-{
- font_color 0 0 0;
-
- part
- {
- graphic NORMAL "red_button";
- graphic ACTIVE "red_button_pressed";
- align 0.5 0.0;
- fill 0.0 0.0;
- };
-
- special "text"
- {
- align 0.5 1.0;
- fill 0.0 0.0;
- };
-};
-
-style "button-arrow_up"
-{
- part
- {
- graphic NORMAL "up_arrow_button";
- graphic ACTIVE "up_arrow_button_pressed";
- align 0.5 0.5;
- fill 0.0 0.0;
- };
-};
-
-style "button-arrow_down"
-{
- part
- {
- graphic NORMAL "down_arrow_button";
- graphic ACTIVE "down_arrow_button_pressed";
- align 0.5 0.5;
- fill 0.0 0.0;
- };
-};
-
-graphic "yellow_lamp"
-{
- texture "gui.png";
- slice 28 105 10 10;
-};
-
-graphic "yellow_lamp_lit"
-{
- texture "gui.png";
- slice 28 117 10 10;
-};
-
-graphic "green_lamp"
-{
- texture "gui.png";
- slice 40 105 10 10;
-};
-
-graphic "green_lamp_lit"
-{
- texture "gui.png";
- slice 40 117 10 10;
-};
-
-graphic "red_lamp"
-{
- texture "gui.png";
- slice 52 105 10 10;
-};
-
-graphic "red_lamp_lit"
-{
- texture "gui.png";
- slice 52 116 10 10;
-};
-
-style "indicator"
-{
- part
- {
- graphic NORMAL "yellow_lamp";
- graphic ACTIVE "yellow_lamp_lit";
- align 0.5 0.5;
- fill 0.0 0.0;
- };
-};
-
-style "indicator-green"
-{
- part
- {
- graphic NORMAL "green_lamp";
- graphic ACTIVE "green_lamp_lit";
- align 0.5 0.5;
- fill 0.0 0.0;
- };
-};
-
-style "indicator-red"
-{
- part
- {
- graphic NORMAL "red_lamp";
- graphic ACTIVE "red_lamp_lit";
- align 0.5 0.5;
- fill 0.0 0.0;
- };
-};
-
-graphic "raised_grey_bg"
-{
- texture "gui.png";
- slice 2 74 24 16;
- border { top 4; right 4; bottom 4; left 4; };
-};
-
-style "panel"
-{
- part
- {
- graphic NORMAL "raised_grey_bg";
- };
- special "children";
-};
-
-style "panel-group"
-{
- special "children";
-};
-
-graphic "sunken_black_bg"
-{
- texture "gui.png";
- slice 2 110 24 16;
- border { top 4; right 4; bottom 4; left 4; };
-};
-
-style "label-digital"
-{
- font "digitalreadout-16.font";
- font_color 0.3 1 0.3;
-
- part
- {
- graphic NORMAL "sunken_black_bg";
- };
-
- special "text"
- {
- fill 0.0 0.0;
- align 0.5 0.5;
- margin { top 1; };
- };
-};
-
-graphic "sunken_white_bg"
-{
- texture "gui.png";
- slice 2 92 24 16;
- border { top 4; right 4; bottom 4; left 4; };
-};
-
-graphic "cursor"
-{
- texture "gui.png";
- slice 40 81 2 10;
-};
-
-style "entry"
-{
- font_color 0 0 0;
-
- part
- {
- graphic NORMAL "sunken_white_bg";
- };
-
- special "text"
- {
- align 0.0 0.5;
- fill 0.0 0.0;
- margin { left 3; right 3; };
- };
-
- special "cursor"
- {
- graphic FOCUS "cursor";
- align 0.0 0.5;
- fill 0.0 0.0;
- size 2 12;
- margin { left 3; right 3; };
- };
-};
-
-style "entry-multiline"
-{
- font_color 0 0 0;
-
- part
- {
- graphic NORMAL "sunken_white_bg";
- };
-
- special "text"
- {
- align 0.0 1.0;
- fill 0.0 0.0;
- margin { top 2; right 13; left 3; bottom 2; };
- };
-
- special "cursor"
- {
- graphic FOCUS "cursor";
- size 2 12;
- };
-
- special "slider"
- {
- align 1.0 0.0;
- fill 0.0 1.0;
- size 10 0;
- margin { top 3; right 2; bottom 3; };
- };
-};
-
-graphic "selection"
-{
- texture "gui.png";
- slice 44 81 20 10;
- border { top 4; right 4; bottom 4; left 4; };
-};
-
-style "list"
-{
- font_color 0.0 0.0 0.0;
-
- part
- {
- graphic NORMAL "sunken_white_bg";
- };
-
- special "selection"
- {
- graphic NORMAL "selection";
- fill 1.0 1.0;
- margin { top 2; right 13; bottom 2; left 2; };
- };
-
- special "items"
- {
- margin { top 2; right 13; bottom 2; left 2; };
- };
-
- special "slider"
- {
- align 1.0 0.0;
- fill 0.0 1.0;
- size 10 0;
- margin { top 3; right 2; bottom 3; };
- };
-};
-
-style "dropdown"
-{
- font_color 0.0 0.0 0.0;
-
- part
- {
- graphic NORMAL "sunken_white_bg";
- margin { right 14; };
- };
-
- part
- {
- graphic NORMAL "down_arrow_button";
- graphic ACTIVE "down_arrow_button_pressed";
- align 1.0 0.5;
- fill 0.0 0.0;
- };
-
- special "text"
- {
- align 0.0 0.5;
- fill 0.0 0.0;
- margin { left 2; };
- };
-
- special "list";
-};
-
-graphic "switch_up"
-{
- texture "gui.png";
- slice 48 42 20 21;
- shadow { top 0; right 2; bottom 3; left 0; };
-};
-
-graphic "switch_down"
-{
- texture "gui.png";
- slice 48 20 20 21;
- shadow { top 0; right 2; bottom 3; left 0; };
-};
-
-style "toggle"
-{
- font_color 0.0 0.0 0.0;
-
- special "text"
- {
- align 0.5 1.0;
- fill 0.0 0.0;
- };
-
- part
- {
- align 0.5 0.0;
- fill 0.0 0.0;
- graphic NORMAL "switch_down";
- graphic ACTIVE "switch_up";
- };
-};
-
-graphic "horizontal_slot"
-{
- texture "gui.png";
- slice 32 97 12 2;
- border { left 1; right 1; };
-};
-
-graphic "horizontal_knob"
-{
- texture "gui.png";
- slice 70 51 32 13;
- shadow { top 0; right 2; bottom 3; left 0; };
-};
-
-style "hslider"
-{
- part
- {
- graphic NORMAL "horizontal_slot";
- align 0.5 0.5;
- fill 1.0 0.0;
- margin { left 4; right 4; };
- };
-
- special "slider"
- {
- graphic NORMAL "horizontal_knob";
- fill 0.0 0.0;
- };
-};
-
-graphic "vertical_slot"
-{
- texture "gui.png";
- slice 32 75 2 12;
- border { top 1; bottom 1; };
-};
-
-graphic "vertical_knob"
-{
- texture "gui.png";
- slice 70 17 12 33;
- shadow { top 0; right 2; bottom 3; left 0; };
-};
-
-style "vslider"
-{
- part
- {
- graphic NORMAL "vertical_slot";
- align 0.5 0.5;
- fill 0.0 1.0;
- margin { top 4; bottom 4; };
- };
-
- special "slider"
- {
- graphic NORMAL "vertical_knob";
- fill 0.0 0.0;
- align 0.0 1.0;
- };
-};
--- /dev/null
+/* $Id$ */
+
+default_font "dejavu-12.font";
+
+style "root"
+{
+ special "children";
+};
+
+style "label"
+{
+ font_color 0 0 0;
+ special "text";
+};
+
+style "image"
+{
+ special "image"
+ {
+ fill 1.0 1.0;
+ };
+};
+
+graphic "tooltip"
+{
+ texture "gui.png";
+ slice 50 93 20 10;
+ border { top 2; right 2; bottom 2; left 2; };
+};
+
+style "label-tooltip"
+{
+ font_color 0.0 0.0 0.0;
+
+ part
+ {
+ graphic NORMAL "tooltip";
+ };
+
+ special "text"
+ {
+ align 0.0 0.5;
+ fill 0.0 0.0;
+ margin { top 2; right 2; bottom 2; left 2; };
+ };
+};
+
+graphic "grey_button"
+{
+ texture "gui.png";
+ slice 2 54 21 10;
+ border { top 2; right 3; bottom 4; left 2; };
+ shadow { top 0; right 1; bottom 2; left 0; };
+};
+
+graphic "grey_button_pressed"
+{
+ texture "gui.png";
+ slice 25 54 21 10;
+ border { top 2; right 3; bottom 4; left 2; };
+ shadow { top 0; right 1; bottom 2; left 0; };
+};
+
+graphic "green_button"
+{
+ texture "gui.png";
+ slice 2 43 21 10;
+ border { top 2; right 3; bottom 4; left 2; };
+ shadow { top 0; right 1; bottom 2; left 0; };
+};
+
+graphic "green_button_pressed"
+{
+ texture "gui.png";
+ slice 25 43 21 10;
+ border { top 2; right 3; bottom 4; left 2; };
+ shadow { top 0; right 1; bottom 2; left 0; };
+};
+
+graphic "red_button"
+{
+ texture "gui.png";
+ slice 2 32 21 10;
+ border { top 2; right 3; bottom 4; left 2; };
+ shadow { top 0; right 1; bottom 2; left 0; };
+};
+
+graphic "red_button_pressed"
+{
+ texture "gui.png";
+ slice 25 32 21 10;
+ border { top 2; right 3; bottom 4; left 2; };
+ shadow { top 0; right 1; bottom 2; left 0; };
+};
+
+graphic "up_arrow_button"
+{
+ texture "gui.png";
+ slice 2 18 13 13;
+ shadow { top 0; right 1; bottom 2; left 0; };
+};
+
+graphic "up_arrow_button_pressed"
+{
+ texture "gui.png";
+ slice 17 18 13 13;
+ shadow { top 0; right 1; bottom 2; left 0; };
+};
+
+graphic "down_arrow_button"
+{
+ texture "gui.png";
+ slice 2 4 13 13;
+ shadow { top 0; right 1; bottom 2; left 0; };
+};
+
+graphic "down_arrow_button_pressed"
+{
+ texture "gui.png";
+ slice 17 4 13 13;
+ shadow { top 0; right 1; bottom 2; left 0; };
+};
+
+style "button"
+{
+ font_color 0 0 0;
+
+ part
+ {
+ graphic NORMAL "grey_button";
+ graphic ACTIVE "grey_button_pressed";
+ align 0.5 0.0;
+ fill 0.0 0.0;
+ };
+
+ special "text"
+ {
+ align 0.5 1.0;
+ fill 0.0 0.0;
+ };
+};
+
+style "button-green"
+{
+ font_color 0 0 0;
+
+ part
+ {
+ graphic NORMAL "green_button";
+ graphic ACTIVE "green_button_pressed";
+ align 0.5 0.0;
+ fill 0.0 0.0;
+ };
+
+ special "text"
+ {
+ align 0.5 1.0;
+ fill 0.0 0.0;
+ };
+};
+
+style "button-red"
+{
+ font_color 0 0 0;
+
+ part
+ {
+ graphic NORMAL "red_button";
+ graphic ACTIVE "red_button_pressed";
+ align 0.5 0.0;
+ fill 0.0 0.0;
+ };
+
+ special "text"
+ {
+ align 0.5 1.0;
+ fill 0.0 0.0;
+ };
+};
+
+style "button-arrow_up"
+{
+ part
+ {
+ graphic NORMAL "up_arrow_button";
+ graphic ACTIVE "up_arrow_button_pressed";
+ align 0.5 0.5;
+ fill 0.0 0.0;
+ };
+};
+
+style "button-arrow_down"
+{
+ part
+ {
+ graphic NORMAL "down_arrow_button";
+ graphic ACTIVE "down_arrow_button_pressed";
+ align 0.5 0.5;
+ fill 0.0 0.0;
+ };
+};
+
+graphic "yellow_lamp"
+{
+ texture "gui.png";
+ slice 28 105 10 10;
+};
+
+graphic "yellow_lamp_lit"
+{
+ texture "gui.png";
+ slice 28 117 10 10;
+};
+
+graphic "green_lamp"
+{
+ texture "gui.png";
+ slice 40 105 10 10;
+};
+
+graphic "green_lamp_lit"
+{
+ texture "gui.png";
+ slice 40 117 10 10;
+};
+
+graphic "red_lamp"
+{
+ texture "gui.png";
+ slice 52 105 10 10;
+};
+
+graphic "red_lamp_lit"
+{
+ texture "gui.png";
+ slice 52 116 10 10;
+};
+
+style "indicator"
+{
+ part
+ {
+ graphic NORMAL "yellow_lamp";
+ graphic ACTIVE "yellow_lamp_lit";
+ align 0.5 0.5;
+ fill 0.0 0.0;
+ };
+};
+
+style "indicator-green"
+{
+ part
+ {
+ graphic NORMAL "green_lamp";
+ graphic ACTIVE "green_lamp_lit";
+ align 0.5 0.5;
+ fill 0.0 0.0;
+ };
+};
+
+style "indicator-red"
+{
+ part
+ {
+ graphic NORMAL "red_lamp";
+ graphic ACTIVE "red_lamp_lit";
+ align 0.5 0.5;
+ fill 0.0 0.0;
+ };
+};
+
+graphic "raised_grey_bg"
+{
+ texture "gui.png";
+ slice 2 74 24 16;
+ border { top 4; right 4; bottom 4; left 4; };
+};
+
+style "panel"
+{
+ part
+ {
+ graphic NORMAL "raised_grey_bg";
+ };
+ special "children";
+};
+
+style "panel-group"
+{
+ special "children";
+};
+
+graphic "sunken_black_bg"
+{
+ texture "gui.png";
+ slice 2 110 24 16;
+ border { top 4; right 4; bottom 4; left 4; };
+};
+
+style "label-digital"
+{
+ font "digitalreadout-16.font";
+ font_color 0.3 1 0.3;
+
+ part
+ {
+ graphic NORMAL "sunken_black_bg";
+ };
+
+ special "text"
+ {
+ fill 0.0 0.0;
+ align 0.5 0.5;
+ margin { top 1; };
+ };
+};
+
+graphic "sunken_white_bg"
+{
+ texture "gui.png";
+ slice 2 92 24 16;
+ border { top 4; right 4; bottom 4; left 4; };
+};
+
+graphic "cursor"
+{
+ texture "gui.png";
+ slice 40 81 2 10;
+};
+
+style "entry"
+{
+ font_color 0 0 0;
+
+ part
+ {
+ graphic NORMAL "sunken_white_bg";
+ };
+
+ special "text"
+ {
+ align 0.0 0.5;
+ fill 0.0 0.0;
+ margin { left 3; right 3; };
+ };
+
+ special "cursor"
+ {
+ graphic FOCUS "cursor";
+ align 0.0 0.5;
+ fill 0.0 0.0;
+ size 2 12;
+ margin { left 3; right 3; };
+ };
+};
+
+style "entry-multiline"
+{
+ font_color 0 0 0;
+
+ part
+ {
+ graphic NORMAL "sunken_white_bg";
+ };
+
+ special "text"
+ {
+ align 0.0 1.0;
+ fill 0.0 0.0;
+ margin { top 2; right 13; left 3; bottom 2; };
+ };
+
+ special "cursor"
+ {
+ graphic FOCUS "cursor";
+ size 2 12;
+ };
+
+ special "slider"
+ {
+ align 1.0 0.0;
+ fill 0.0 1.0;
+ size 10 0;
+ margin { top 3; right 2; bottom 3; };
+ };
+};
+
+graphic "selection"
+{
+ texture "gui.png";
+ slice 44 81 20 10;
+ border { top 4; right 4; bottom 4; left 4; };
+};
+
+style "list"
+{
+ font_color 0.0 0.0 0.0;
+
+ part
+ {
+ graphic NORMAL "sunken_white_bg";
+ };
+
+ special "selection"
+ {
+ graphic NORMAL "selection";
+ fill 1.0 1.0;
+ margin { top 2; right 13; bottom 2; left 2; };
+ };
+
+ special "items"
+ {
+ margin { top 2; right 13; bottom 2; left 2; };
+ };
+
+ special "slider"
+ {
+ align 1.0 0.0;
+ fill 0.0 1.0;
+ size 10 0;
+ margin { top 3; right 2; bottom 3; };
+ };
+};
+
+style "dropdown"
+{
+ font_color 0.0 0.0 0.0;
+
+ part
+ {
+ graphic NORMAL "sunken_white_bg";
+ margin { right 14; };
+ };
+
+ part
+ {
+ graphic NORMAL "down_arrow_button";
+ graphic ACTIVE "down_arrow_button_pressed";
+ align 1.0 0.5;
+ fill 0.0 0.0;
+ };
+
+ special "text"
+ {
+ align 0.0 0.5;
+ fill 0.0 0.0;
+ margin { left 2; };
+ };
+
+ special "list";
+};
+
+graphic "switch_up"
+{
+ texture "gui.png";
+ slice 48 42 20 21;
+ shadow { top 0; right 2; bottom 3; left 0; };
+};
+
+graphic "switch_down"
+{
+ texture "gui.png";
+ slice 48 20 20 21;
+ shadow { top 0; right 2; bottom 3; left 0; };
+};
+
+style "toggle"
+{
+ font_color 0.0 0.0 0.0;
+
+ special "text"
+ {
+ align 0.5 1.0;
+ fill 0.0 0.0;
+ };
+
+ part
+ {
+ align 0.5 0.0;
+ fill 0.0 0.0;
+ graphic NORMAL "switch_down";
+ graphic ACTIVE "switch_up";
+ };
+};
+
+graphic "horizontal_slot"
+{
+ texture "gui.png";
+ slice 32 97 12 2;
+ border { left 1; right 1; };
+};
+
+graphic "horizontal_knob"
+{
+ texture "gui.png";
+ slice 70 51 32 13;
+ shadow { top 0; right 2; bottom 3; left 0; };
+};
+
+style "hslider"
+{
+ part
+ {
+ graphic NORMAL "horizontal_slot";
+ align 0.5 0.5;
+ fill 1.0 0.0;
+ margin { left 4; right 4; };
+ };
+
+ special "slider"
+ {
+ graphic NORMAL "horizontal_knob";
+ fill 0.0 0.0;
+ };
+};
+
+graphic "vertical_slot"
+{
+ texture "gui.png";
+ slice 32 75 2 12;
+ border { top 1; bottom 1; };
+};
+
+graphic "vertical_knob"
+{
+ texture "gui.png";
+ slice 70 17 12 33;
+ shadow { top 0; right 2; bottom 3; left 0; };
+};
+
+style "vslider"
+{
+ part
+ {
+ graphic NORMAL "vertical_slot";
+ align 0.5 0.5;
+ fill 0.0 1.0;
+ margin { top 4; bottom 4; };
+ };
+
+ special "slider"
+ {
+ graphic NORMAL "vertical_knob";
+ fill 0.0 0.0;
+ align 0.0 1.0;
+ };
+};
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
using namespace Msp;
-namespace Marklin {
+namespace R2C2 {
Catalogue3D::Catalogue3D(Catalogue &c):
catalogue(c),
bld.end();
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLIN3D_CATALOGUE_H_
-#define MARKLIN3D_CATALOGUE_H_
+#ifndef R2C2_3D_CATALOGUE_H_
+#define R2C2_3D_CATALOGUE_H_
#include <msp/gl/material.h>
#include <msp/gl/mesh.h>
-#include "libmarklin/catalogue.h"
+#include "libr2c2/catalogue.h"
-namespace Marklin {
+namespace R2C2 {
class TrackType3D;
class VehicleType3D;
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace Msp;
-namespace Marklin {
+namespace R2C2 {
Endpoint3D::Endpoint3D(const Track3D &t, unsigned i):
track(t),
}
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLIN3D_ENDPOINT_H_
-#define MARKLIN3D_ENDPOINT_H_
+#ifndef R2C2_3D_ENDPOINT_H_
+#define R2C2_3D_ENDPOINT_H_
#include <msp/gl/mesh.h>
#include <msp/gl/renderable.h>
-#include "libmarklin/track.h"
+#include "libr2c2/track.h"
-namespace Marklin {
+namespace R2C2 {
class Track3D;
virtual void render(const Msp::GL::Tag &) const;
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
using namespace Msp;
-namespace Marklin {
+namespace R2C2 {
Layout3D::Layout3D(Layout &l):
layout(l),
delete i->second;
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLIN3D_LAYOUT_H_
-#define MARKLIN3D_LAYOUT_H_
+#ifndef R2C2_3D_LAYOUT_H_
+#define R2C2_3D_LAYOUT_H_
#include <sigc++/trackable.h>
#include <msp/gl/scene.h>
-#include "libmarklin/layout.h"
+#include "libr2c2/layout.h"
#include "catalogue.h"
-namespace Marklin {
+namespace R2C2 {
class Track3D;
class Vehicle3D;
void vehicle_removed(Vehicle &);
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLIN3D_OBJECT_H_
-#define MARKLIN3D_OBJECT_H_
+#ifndef R2C2_3D_OBJECT_H_
+#define R2C2_3D_OBJECT_H_
-#include "libmarklin/geometry.h"
+#include "libr2c2/geometry.h"
-namespace Marklin {
+namespace R2C2 {
class Object3D
{
virtual bool is_visible() const = 0;
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
using namespace Msp;
-namespace Marklin {
+namespace R2C2 {
Overlay3D::Overlay3D(const Graphics::Window &w, const GL::Camera &c, const GL::Font &f):
window(w),
background((GL::COLOR4_UBYTE, GL::VERTEX2))
{ }
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLIN3D_OVERLAY_H_
-#define MARKLIN3D_OVERLAY_H_
+#ifndef R2C2_3D_OVERLAY_H_
+#define R2C2_3D_OVERLAY_H_
#include <msp/gbase/window.h>
#include <msp/gl/camera.h>
#include <msp/gl/mesh.h>
#include <msp/gl/renderable.h>
-namespace Marklin {
+namespace R2C2 {
class Object3D;
void update_icon(Icon &);
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gl/matrix.h>
-#include "libmarklin/tracktype.h"
+#include "libr2c2/tracktype.h"
#include "layout.h"
#include "path.h"
#include "track.h"
using namespace Msp;
-namespace Marklin {
+namespace R2C2 {
Path3D::Path3D(const Track3D &t):
track(t),
}
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLIN3D_PATH_H_
-#define MARKLIN3D_PATH_H_
+#ifndef R2C2_3D_PATH_H_
+#define R2C2_3D_PATH_H_
#include <msp/gl/color.h>
#include <msp/gl/mesh.h>
#include <msp/gl/renderable.h>
-namespace Marklin {
+namespace R2C2 {
class Track3D;
virtual void render(const Msp::GL::Tag &) const;
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <cmath>
#include <msp/gl/matrix.h>
#include <msp/gl/misc.h>
-#include "libmarklin/tracktype.h"
+#include "libr2c2/tracktype.h"
#include "endpoint.h"
#include "layout.h"
#include "path.h"
using namespace std;
using namespace Msp;
-namespace Marklin {
+namespace R2C2 {
Track3D::Track3D(Layout3D &l, Track &t):
layout(l),
glPopName();
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLIN3D_TRACK_H_
-#define MARKLIN3D_TRACK_H_
+#ifndef R2C2_3D_TRACK_H_
+#define R2C2_3D_TRACK_H_
#include <list>
#include <msp/gl/renderable.h>
#include <msp/gl/vertexarray.h>
#include <msp/gl/vertexarraybuilder.h>
-#include "libmarklin/track.h"
-#include "libmarklin/trackpart.h"
+#include "libr2c2/track.h"
+#include "libr2c2/trackpart.h"
#include "object.h"
-namespace Marklin {
+namespace R2C2 {
class Endpoint3D;
class Layout3D;
virtual void render(const Msp::GL::Tag &) const;
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
namespace {
-bool compare_z(const Marklin::Point &p1, const Marklin::Point &p2)
+bool compare_z(const R2C2::Point &p1, const R2C2::Point &p2)
{
return p1.z<p2.z;
}
// http://en.wikipedia.org/wiki/Graham_scan
// Find point with lowest X coordinate
- Marklin::Point lowest = *begin;
+ R2C2::Point lowest = *begin;
for(Iter i=begin; i!=end; ++i)
if(i->x<lowest.x || (i->x==lowest.x && i->y>lowest.y))
lowest = *i;
}
-namespace Marklin {
+namespace R2C2 {
TrackType3D::TrackType3D(const Catalogue3D &cat3d, const TrackType &tt):
catalogue(cat3d),
base_index += (nsegs+1)*(n_points-1)*2;
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLIN3D_TRACKTYPE_H_
-#define MARKLIN3D_TRACKTYPE_H_
+#ifndef R2C2_3D_TRACKTYPE_H_
+#define R2C2_3D_TRACKTYPE_H_
#include <msp/gl/mesh.h>
#include <msp/gl/meshbuilder.h>
#include <msp/gl/renderable.h>
-#include "libmarklin/profile.h"
-#include "libmarklin/tracktype.h"
+#include "libr2c2/profile.h"
+#include "libr2c2/tracktype.h"
-namespace Marklin {
+namespace R2C2 {
class Catalogue3D;
void build_part(const TrackPart &, const Profile &, const Point &, Msp::GL::MeshBuilder &, unsigned &);
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
using namespace Msp;
-namespace Marklin {
+namespace R2C2 {
Vehicle3D::Vehicle3D(Layout3D &l, Vehicle &v):
layout(l),
}
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef LIBMARKLIN3D_VEHICLE_H_
-#define LIBMARKLIN3D_VEHICLE_H_
+#ifndef LIBR2C23D_VEHICLE_H_
+#define LIBR2C23D_VEHICLE_H_
#include <msp/gl/renderable.h>
-#include "libmarklin/vehicle.h"
+#include "libr2c2/vehicle.h"
#include "object.h"
-namespace Marklin {
+namespace R2C2 {
class Layout3D;
class VehicleType3D;
virtual void render(const Msp::GL::Tag &) const;
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
}
-namespace Marklin {
+namespace R2C2 {
VehicleType3D::VehicleType3D(const Catalogue3D &c, const VehicleType &t):
catalogue(c),
return mesh;
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef LIBMARKLIN3D_VEHICLETYPE_H_
-#define LIBMARKLIN3D_VEHICLETYPE_H_
+#ifndef LIBR2C23D_VEHICLETYPE_H_
+#define LIBR2C23D_VEHICLETYPE_H_
#include <msp/gl/mesh.h>
#include <msp/gl/object.h>
-#include "libmarklin/vehicletype.h"
+#include "libr2c2/vehicletype.h"
-namespace Marklin {
+namespace R2C2 {
class Catalogue3D;
Msp::GL::Mesh *create_flat_wagon(const std::map<std::string, std::string> &);
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
CameraController::CameraController(Designer &d, Graphics::EventSource &es, GL::Camera &c):
designer(d),
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/strings/utils.h>
#include <msp/time/units.h>
#include <msp/time/utils.h>
-#include "libmarklin/route.h"
-#include "libmarklin/tracktype.h"
+#include "libr2c2/route.h"
+#include "libr2c2/tracktype.h"
#include "3d/path.h"
#include "designer.h"
#include "input.h"
#include "toolbar.h"
using namespace std;
-using namespace Marklin;
+using namespace R2C2;
using namespace Msp;
Application::RegApp<Designer> Designer::reg;
Designer::Designer(int argc, char **argv):
window(1280, 960),
- ui_res("marklin.res"),
+ ui_res("r2c2.res"),
root(ui_res, window),
base_object(0),
cur_route(0),
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/resources.h>
#include <msp/gltk/root.h>
#include <msp/time/timestamp.h>
-#include "libmarklin/catalogue.h"
-#include "libmarklin/layout.h"
+#include "libr2c2/catalogue.h"
+#include "libr2c2/layout.h"
#include "3d/layout.h"
#include "3d/overlay.h"
#include "3d/track.h"
Toolbar *toolbar;
Msp::GLtk::Label *lbl_status;
- Marklin::Catalogue catalogue;
- Marklin::Layout3D *cat_layout_3d;
+ R2C2::Catalogue catalogue;
+ R2C2::Layout3D *cat_layout_3d;
std::string filename;
- Marklin::Layout *layout;
- Marklin::Layout3D *layout_3d;
- Marklin::Overlay3D *overlay;
+ R2C2::Layout *layout;
+ R2C2::Layout3D *layout_3d;
+ R2C2::Overlay3D *overlay;
Msp::GL::Object *base_object;
- Marklin::Route *cur_route;
- std::list<Marklin::Track *> new_tracks;
+ R2C2::Route *cur_route;
+ std::list<R2C2::Track *> new_tracks;
Msp::GL::Pipeline *pipeline;
Msp::GL::Camera camera;
Msp::GL::Lighting lighting;
void save();
void quit();
- const Marklin::Catalogue &get_catalogue() const { return catalogue; }
- Marklin::Layout &get_layout() { return *layout; }
- Marklin::Layout3D &get_layout_3d() { return *layout_3d; }
+ const R2C2::Catalogue &get_catalogue() const { return catalogue; }
+ R2C2::Layout &get_layout() { return *layout; }
+ R2C2::Layout3D &get_layout_3d() { return *layout_3d; }
const CameraController &get_camera_controller() const { return camera_ctl; }
const Msp::GLtk::Resources &get_ui_resources() const { return ui_res; }
Msp::GLtk::Root &get_root() { return root; }
void rename_route();
void svg_export();
- void edit_route(Marklin::Route *);
- Marklin::Route *get_current_route() const { return cur_route; }
+ void edit_route(R2C2::Route *);
+ R2C2::Route *get_current_route() const { return cur_route; }
void add_selection_to_route();
- Marklin::Point map_pointer_to_ground(int, int);
+ R2C2::Point map_pointer_to_ground(int, int);
private:
void tick();
void key_press(unsigned, unsigned, wchar_t);
void pointer_motion(int, int);
void apply_camera();
void render();
- void track_added(Marklin::Track &);
- void track_removed(Marklin::Track &);
- Marklin::Track3D *pick_track(int, int);
- void update_track_icon(Marklin::Track3D &);
+ void track_added(R2C2::Track &);
+ void track_removed(R2C2::Track &);
+ R2C2::Track3D *pick_track(int, int);
+ void update_track_icon(R2C2::Track3D &);
void selection_changed();
void manipulation_status(const std::string &);
void manipulation_done(bool);
void route_name_accept(const std::string &);
void svg_export_accept(const std::string &);
std::string tooltip(int, int);
- void show_route(const Marklin::Route *);
+ void show_route(const R2C2::Route *);
};
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2008, 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2008, 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <algorithm>
#include <cmath>
#include <msp/strings/formatter.h>
-#include "libmarklin/tracktype.h"
+#include "libr2c2/tracktype.h"
#include "designer.h"
#include "manipulator.h"
#include "selection.h"
using namespace std;
-using namespace Marklin;
+using namespace R2C2;
using namespace Msp;
Manipulator::Manipulator(Designer &d, Graphics::EventSource &es, Selection &s):
}
}
-vector<Track *> Manipulator::create_straight(const Marklin::Point &start, float dir, float length, float limit)
+vector<Track *> Manipulator::create_straight(const R2C2::Point &start, float dir, float length, float limit)
{
const Catalogue::TrackMap &track_types = designer.get_catalogue().get_tracks();
std::map<float, const TrackType *> types_by_length;
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
struct MTrack
{
- Marklin::Track *track;
- Marklin::Point pos;
+ R2C2::Track *track;
+ R2C2::Point pos;
float rot;
- MTrack(Marklin::Track *);
+ MTrack(R2C2::Track *);
};
struct TrackOrder
{
- Marklin::Track *track;
+ R2C2::Track *track;
bool rev;
- TrackOrder(Marklin::Track *t, bool r): track(t), rev(r) { }
+ TrackOrder(R2C2::Track *t, bool r): track(t), rev(r) { }
};
public:
Msp::Graphics::EventSource &event_source;
Selection &selection;
std::vector<MTrack> tracks;
- Marklin::Point center;
+ R2C2::Point center;
- Marklin::Point gpointer;
+ R2C2::Point gpointer;
int pointer_y;
Mode mode;
- Marklin::Point move_origin;
+ R2C2::Point move_origin;
float angle;
float rot_origin;
int elev_origin;
- std::set<Marklin::Track *> neighbors;
- std::vector<Marklin::Track *> extend_tracks;
+ std::set<R2C2::Track *> neighbors;
+ std::vector<R2C2::Track *> extend_tracks;
public:
Manipulator(Designer &, Msp::Graphics::EventSource &, Selection &);
void update_tracks();
void update_neighbors();
void set_slope(TrackOrder &, float, float);
- std::vector<Marklin::Track *> create_straight(const Marklin::Point &, float, float, float);
+ std::vector<R2C2::Track *> create_straight(const R2C2::Point &, float, float, float);
};
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include "measure.h"
using namespace std;
-using namespace Marklin;
+using namespace R2C2;
using namespace Msp;
Measure::Measure(Designer &d):
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
private:
Designer &designer;
- Marklin::Point pointer;
- Marklin::Point spoint;
+ R2C2::Point pointer;
+ R2C2::Point spoint;
float sdir;
float par_dist;
float perp_dist;
void pointer_motion(int, int, float, float);
void render();
private:
- void snap_to_tracks(Marklin::Point &, float &);
+ void snap_to_tracks(R2C2::Point &, float &);
};
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <algorithm>
-#include "libmarklin/block.h"
+#include "libr2c2/block.h"
#include "selection.h"
using namespace std;
-using namespace Marklin;
+using namespace R2C2;
using namespace Msp;
Track *Selection::get_track() const
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
sigc::signal<void> signal_changed;
private:
- std::set<Marklin::Track *> tracks;
+ std::set<R2C2::Track *> tracks;
public:
- const std::set<Marklin::Track *> &get_tracks() const { return tracks; }
- Marklin::Track *get_track() const;
+ const std::set<R2C2::Track *> &get_tracks() const { return tracks; }
+ R2C2::Track *get_track() const;
unsigned size() const { return tracks.size(); }
void clear();
signal_changed.emit();
}
- void add_track(Marklin::Track *);
- void remove_track(Marklin::Track *);
- void toggle_track(Marklin::Track *);
+ void add_track(R2C2::Track *);
+ void remove_track(R2C2::Track *);
+ void toggle_track(R2C2::Track *);
void select_more();
void select_linked();
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <cmath>
#include <msp/strings/formatter.h>
-#include "libmarklin/catalogue.h"
-#include "libmarklin/track.h"
-#include "libmarklin/tracktype.h"
+#include "libr2c2/catalogue.h"
+#include "libr2c2/track.h"
+#include "libr2c2/tracktype.h"
#include "svgexporter.h"
using namespace std;
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
SvgExporter::SvgExporter(const Layout &l):
layout(l),
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#define SVGEXPORTER_H_
#include <libxml++/libxml++.h>
-#include "libmarklin/layout.h"
+#include "libr2c2/layout.h"
class SvgExporter
{
private:
- const Marklin::Layout &layout;
+ const R2C2::Layout &layout;
float gauge;
float rail_width;
public:
- SvgExporter(const Marklin::Layout &);
+ SvgExporter(const R2C2::Layout &);
void save(const std::string &);
private:
- void save_track(const Marklin::Track &, xmlpp::Element &);
+ void save_track(const R2C2::Track &, xmlpp::Element &);
};
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/button.h>
#include <msp/strings/formatter.h>
-#include "libmarklin/route.h"
+#include "libr2c2/route.h"
#include "designer.h"
#include "toolbar.h"
using namespace std;
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
Toolbar::Toolbar(Designer &d):
designer(d)
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <sigc++/trackable.h>
#include <msp/gltk/dropdown.h>
#include <msp/gltk/panel.h>
-#include "libmarklin/route.h"
+#include "libr2c2/route.h"
class Designer;
private:
void route_selected(unsigned, const std::string &);
void delete_route_clicked();
- void route_added(Marklin::Route &);
+ void route_added(R2C2::Route &);
void update_routes();
};
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
TrackWrap::TrackWrap(Designer &d, Selection &s):
designer(d),
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gl/mesh.h>
#include <msp/gl/renderable.h>
-#include "libmarklin/track.h"
+#include "libr2c2/track.h"
class Designer;
class Selection;
private:
struct Wrap
{
- Marklin::Track *track;
+ R2C2::Track *track;
Msp::GL::Mesh *mesh;
};
Designer &designer;
Selection &selection;
- std::map<const Marklin::TrackType *, Msp::GL::Mesh *> meshes;
+ std::map<const R2C2::TrackType *, Msp::GL::Mesh *> meshes;
std::list<Wrap> wraps;
public:
private:
void selection_changed();
- Msp::GL::Mesh &get_mesh(const Marklin::TrackType &);
+ Msp::GL::Mesh &get_mesh(const R2C2::TrackType &);
};
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/strings/formatter.h>
#include <msp/time/units.h>
#include <msp/time/utils.h>
-#include "libmarklin/driver.h"
-#include "libmarklin/tracktype.h"
+#include "libr2c2/driver.h"
+#include "libr2c2/tracktype.h"
#include "3d/path.h"
#include "3d/track.h"
#include "3d/vehicle.h"
#include "trainview.h"
using namespace std;
-using namespace Marklin;
+using namespace R2C2;
using namespace Msp;
Application::RegApp<Engineer> Engineer::reg;
window.set_title("Railroad Engineer");
window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Engineer::exit), 0));
- DataFile::load(ui_res, "marklin.res");
+ DataFile::load(ui_res, "r2c2.res");
root = new GLtk::Root(ui_res, window);
root->signal_button_press.connect(sigc::mem_fun(this, &Engineer::button_press));
root->signal_pointer_motion.connect(sigc::mem_fun(this, &Engineer::pointer_motion));
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gl/pipeline.h>
#include <msp/gltk/resources.h>
#include <msp/gltk/root.h>
-#include "libmarklin/catalogue.h"
-#include "libmarklin/train.h"
+#include "libr2c2/catalogue.h"
+#include "libr2c2/train.h"
#include "3d/layout.h"
#include "3d/overlay.h"
#include "3d/path.h"
class Engineer: public Msp::Application
{
public:
- sigc::signal<void, Marklin::Track *, int> signal_pick_done;
+ sigc::signal<void, R2C2::Track *, int> signal_pick_done;
private:
Options options;
Msp::GLtk::Resources ui_res;
Msp::GLtk::Root *root;
- Marklin::Catalogue catalogue;
- Marklin::Layout layout;
- Marklin::Layout3D layout_3d;
- Marklin::Server *server;
+ R2C2::Catalogue catalogue;
+ R2C2::Layout layout;
+ R2C2::Layout3D layout_3d;
+ R2C2::Server *server;
Msp::IO::EventDispatcher event_disp;
- Marklin::Overlay3D *overlay;
- std::map<Marklin::Train *, Msp::GL::Color> train_colors;
+ R2C2::Overlay3D *overlay;
+ std::map<R2C2::Train *, Msp::GL::Color> train_colors;
Msp::GL::Camera camera;
Msp::GL::Lighting lighting;
std::list<TrainView *> train_views;
Msp::Time::TimeStamp status_timeout;
bool picking;
- Marklin::Track *picking_track;
+ R2C2::Track *picking_track;
int picking_entry;
- Marklin::Path3D *picking_path;
+ R2C2::Path3D *picking_path;
int pointer_x;
int pointer_y;
bool pointer_moved;
const Msp::GLtk::Resources &get_ui_resources() const { return ui_res; }
Msp::GLtk::Root &get_root() const { return *root; }
- const Marklin::Catalogue &get_catalogue() const { return catalogue; }
- Marklin::Layout &get_layout() { return layout; }
- Marklin::Layout3D &get_layout_3d() { return layout_3d; }
+ const R2C2::Catalogue &get_catalogue() const { return catalogue; }
+ R2C2::Layout &get_layout() { return layout; }
+ R2C2::Layout3D &get_layout_3d() { return layout_3d; }
const Msp::GL::Lighting &get_lighting() const { return lighting; }
void set_status(const std::string &);
void rearrange_panels();
void button_press(int, int, unsigned, unsigned);
void pointer_motion(int, int);
void view_all();
- void set_block_color(const Marklin::Block &, const Msp::GL::Color &);
- void reset_block_color(const Marklin::Block &);
+ void set_block_color(const R2C2::Block &, const Msp::GL::Color &);
+ void reset_block_color(const R2C2::Block &);
void sensor_event(unsigned, bool);
- void block_reserved(const Marklin::Block &, const Marklin::Train *);
- Marklin::Track3D *pick_track(int, int);
- void train_added(Marklin::Train &);
+ void block_reserved(const R2C2::Block &, const R2C2::Train *);
+ R2C2::Track3D *pick_track(int, int);
+ void train_added(R2C2::Train &);
virtual void sighandler(int);
static Msp::Application::RegApp<Engineer> reg;
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/button.h>
-#include "libmarklin/driver.h"
+#include "libr2c2/driver.h"
#include "engineer.h"
#include "mainpanel.h"
#include "trainproperties.h"
lbl_status->set_geometry(GLtk::Geometry(10, 10, 180, 20));
lbl_status->set_style("digital");
- Marklin::Driver &driver = engineer.get_layout().get_driver();
+ R2C2::Driver &driver = engineer.get_layout().get_driver();
if(driver.get_power())
ind_on->set_active(true);
else
void MainPanel::toggle_halt()
{
- Marklin::Driver &driver = engineer.get_layout().get_driver();
+ R2C2::Driver &driver = engineer.get_layout().get_driver();
driver.halt(!driver.is_halted());
}
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/label.h>
#include "engineer.h"
-#include "libmarklin/route.h"
+#include "libr2c2/route.h"
#include "routeselect.h"
using namespace std;
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
RouteSelect::RouteSelect(Engineer &e, Train &t):
engineer(e),
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#define ROUTESELECT_H_
#include <msp/gltk/dropdown.h>
-#include "libmarklin/train.h"
+#include "libr2c2/train.h"
#include "dialog.h"
class Engineer;
{
private:
Engineer &engineer;
- Marklin::Train &train;
+ R2C2::Train &train;
Msp::GLtk::Dropdown *drp_route;
public:
- RouteSelect(Engineer &, Marklin::Train &);
+ RouteSelect(Engineer &, R2C2::Train &);
private:
virtual void on_ok_clicked();
};
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
TimetableDialog::TimetableDialog(Timetable &tt):
timetable(tt)
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/dialog.h>
#include <msp/gltk/entry.h>
#include <msp/gltk/toggle.h>
-#include "libmarklin/timetable.h"
+#include "libr2c2/timetable.h"
class TimetableDialog: public Msp::GLtk::Dialog
{
private:
- Marklin::Timetable &timetable;
+ R2C2::Timetable &timetable;
Msp::GLtk::Entry *ent_timetable;
Msp::GLtk::Toggle *tgl_enabled;
public:
- TimetableDialog(Marklin::Timetable &);
+ TimetableDialog(R2C2::Timetable &);
private:
void enabled_toggled(bool);
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <cmath>
#include <msp/strings/formatter.h>
-#include "libmarklin/timetable.h"
-#include "libmarklin/trackiter.h"
-#include "libmarklin/vehicletype.h"
+#include "libr2c2/timetable.h"
+#include "libr2c2/trackiter.h"
+#include "libr2c2/vehicletype.h"
#include "engineer.h"
#include "routeselect.h"
#include "timetabledialog.h"
using namespace std;
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
TrainPanel::TrainPanel(Engineer &e, Train &t):
engineer(e),
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/label.h>
#include <msp/gltk/panel.h>
#include <msp/gltk/toggle.h>
-#include "libmarklin/route.h"
-#include "libmarklin/train.h"
+#include "libr2c2/route.h"
+#include "libr2c2/train.h"
class Engineer;
{
private:
Engineer &engineer;
- Marklin::Train &train;
+ R2C2::Train &train;
Msp::GLtk::Panel *pnl_basic;
Msp::GLtk::Panel *pnl_extra;
Msp::GLtk::Button *btn_expand;
bool expanded;
public:
- TrainPanel(Engineer &, Marklin::Train &);
+ TrainPanel(Engineer &, R2C2::Train &);
void expand(bool = true);
private:
void train_control_changed(const std::string &, float);
void train_function_changed(unsigned, bool);
- void train_route_changed(const Marklin::Route *);
+ void train_route_changed(const R2C2::Route *);
void train_status_changed(const std::string &);
void place_clicked();
void take_clicked();
void speed_slider_changed(double);
void forward_toggled(bool);
void func_toggled(bool, unsigned);
- void place(Marklin::Track *, unsigned);
- void go_to(Marklin::Track *, unsigned);
+ void place(R2C2::Track *, unsigned);
+ void go_to(R2C2::Track *, unsigned);
};
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/label.h>
#include <msp/strings/formatter.h>
#include <msp/strings/lexicalcast.h>
-#include "libmarklin/driver.h"
-#include "libmarklin/vehicle.h"
-#include "libmarklin/vehicletype.h"
+#include "libr2c2/driver.h"
+#include "libr2c2/vehicle.h"
+#include "libr2c2/vehicletype.h"
#include "engineer.h"
#include "trainproperties.h"
using namespace std;
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
TrainProperties::TrainProperties(Engineer &e, Train *t):
engineer(e),
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/dropdown.h>
#include <msp/gltk/entry.h>
-#include <libmarklin/train.h>
+#include <libr2c2/train.h>
#include "dialog.h"
class Engineer;
{
private:
Engineer &engineer;
- Marklin::Train *train;
+ R2C2::Train *train;
Msp::GLtk::Entry *ent_addr;
Msp::GLtk::Dropdown *drp_protocol;
Msp::GLtk::Dropdown *drp_type;
Msp::GLtk::Dropdown *drp_priority;
Msp::GLtk::List *lst_vehicles;
Msp::GLtk::Dropdown *drp_new_vehicle;
- std::vector<const Marklin::VehicleType *> add_vehicles;
+ std::vector<const R2C2::VehicleType *> add_vehicles;
std::set<unsigned> rem_vehicles;
public:
- TrainProperties(Engineer &, Marklin::Train *);
+ TrainProperties(Engineer &, R2C2::Train *);
private:
virtual void on_ok_clicked();
void new_vehicle_selected(unsigned, const std::string &);
void remove_vehicle_clicked();
- const Marklin::VehicleType &get_vehicle_type(unsigned, bool);
+ const R2C2::VehicleType &get_vehicle_type(unsigned, bool);
};
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gl/tests.h>
#include <msp/gltk/button.h>
#include <msp/gltk/image.h>
-#include "libmarklin/vehicle.h"
-#include "libmarklin/vehicletype.h"
+#include "libr2c2/vehicle.h"
+#include "libr2c2/vehicletype.h"
#include "engineer.h"
#include "trainview.h"
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
TrainView::TrainView(Engineer &e, const Train &t):
engineer(e),
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gl/texture2d.h>
#include <msp/gltk/panel.h>
#include <msp/gltk/toggle.h>
-#include "libmarklin/train.h"
+#include "libr2c2/train.h"
class Engineer;
private:
Engineer &engineer;
- const Marklin::Train &train;
+ const R2C2::Train &train;
Msp::GLtk::Toggle *tgl_forward;
Mode mode;
bool forward;
bool stale;
public:
- TrainView(Engineer &, const Marklin::Train &);
+ TrainView(Engineer &, const R2C2::Train &);
~TrainView();
void set_mode(Mode);
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include "aicontrol.h"
-#include "catalogue.h"
-#include "layout.h"
-#include "train.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-AIControl::AIControl(Train &t, Controller *n):
- train(t),
- next_ctrl(n),
- target_speed(Control::continuous("speed", 0, 1000)),
- blocked(false),
- approach(false)
-{
- target_speed.set(0);
-
- train.signal_arrived.connect(sigc::mem_fun(this, &AIControl::arrived));
- next_ctrl->signal_control_changed.connect(sigc::mem_fun(this, &AIControl::control_changed));
-}
-
-AIControl::~AIControl()
-{
- delete next_ctrl;
-}
-
-void AIControl::set_control(const string &n, float v)
-{
- if(n=="speed")
- {
- if(v && !train.is_active())
- train.set_active(true);
-
- target_speed.set(v);
- if(!blocked)
- {
- float approach_speed = 5*train.get_layout().get_catalogue().get_scale();
- if(approach && target_speed.value>approach_speed)
- next_ctrl->set_control("speed", approach_speed);
- else
- next_ctrl->set_control("speed", target_speed.value);
- }
-
- signal_control_changed.emit(target_speed);
- }
- else
- next_ctrl->set_control(n, v);
-}
-
-const Controller::Control &AIControl::get_control(const string &n) const
-{
- if(n=="speed")
- return target_speed;
- else
- return next_ctrl->get_control(n);
-}
-
-float AIControl::get_speed() const
-{
- return next_ctrl->get_speed();
-}
-
-bool AIControl::get_reverse() const
-{
- return next_ctrl->get_reverse();
-}
-
-float AIControl::get_braking_distance() const
-{
- return next_ctrl->get_braking_distance();
-}
-
-void AIControl::tick(const Time::TimeDelta &dt)
-{
- float scale = train.get_layout().get_catalogue().get_scale();
- float rsv_dist = train.get_reserved_distance();
- float brake_dist = next_ctrl->get_braking_distance();
- float approach_margin = 50*scale;
- float approach_speed = 5*scale;
- float margin = 10*scale;
-
- if(!blocked && rsv_dist<brake_dist+margin)
- {
- blocked = true;
- next_ctrl->set_control("speed", 0);
- }
- else if((!approach && rsv_dist<brake_dist*1.3+approach_margin) || (blocked && rsv_dist>brake_dist+margin*2))
- {
- blocked = false;
- approach = true;
- if(target_speed.value>approach_speed)
- next_ctrl->set_control("speed", approach_speed);
- else
- next_ctrl->set_control("speed", target_speed.value);
- }
- else if((blocked || approach) && rsv_dist>brake_dist*1.3+approach_margin*2)
- {
- blocked = false;
- approach = false;
- next_ctrl->set_control("speed", target_speed.value);
- }
-
- next_ctrl->tick(dt);
-
- if(!target_speed.value && !next_ctrl->get_speed() && train.is_active())
- train.set_active(false);
-}
-
-void AIControl::control_changed(const Control &ctrl)
-{
- if(ctrl.name!="speed")
- signal_control_changed.emit(ctrl);
-}
-
-void AIControl::arrived()
-{
- set_control("speed", 0);
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_AICONTROL_H_
-#define LIBMARKLIN_AICONTROL_H_
-
-#include <sigc++/trackable.h>
-#include "controller.h"
-
-namespace Marklin {
-
-class Train;
-
-class AIControl: public Controller, public sigc::trackable
-{
-private:
- Train &train;
- Controller *next_ctrl;
- Control target_speed;
- bool blocked;
- bool approach;
-
-public:
- AIControl(Train &, Controller *);
- virtual ~AIControl();
-
- virtual void set_control(const std::string &, float);
- virtual const Control &get_control(const std::string &) const;
-
- virtual float get_speed() const;
- virtual bool get_reverse() const;
- virtual float get_braking_distance() const;
-
- virtual void tick(const Msp::Time::TimeDelta &);
-
-private:
- void control_changed(const Control &);
- void arrived();
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <msp/strings/utils.h>
-#include "articlenumber.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-ArticleNumber::ArticleNumber(unsigned n)
-{
- Part part;
- part.number = n;
- part.letter = 0;
- parts.push_back(part);
-}
-
-ArticleNumber::ArticleNumber(const string &s)
-{
- vector<string> sparts = split(s, '-');
- for(vector<string>::iterator i=sparts.begin(); i!=sparts.end(); ++i)
- {
- if(i->empty())
- throw InvalidParameterValue("Malformed article number");
-
- unsigned nondigit = i->size();
- for(unsigned j=0; j<i->size(); ++j)
- if(!isdigit((*i)[j]))
- {
- nondigit = j;
- break;
- }
-
- if(!nondigit || nondigit<i->size()-1)
- throw InvalidParameterValue("Malformed article number");
-
- Part part;
- part.number = lexical_cast<unsigned>(i->substr(0, nondigit));
- part.letter = nondigit<i->size() ? (*i)[nondigit] : 0;
- parts.push_back(part);
- }
-}
-
-string ArticleNumber::str() const
-{
- string result;
- for(vector<Part>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
- {
- if(!result.empty())
- result += '-';
-
- result += lexical_cast(i->number);
- if(i->letter)
- result += i->letter;
- }
-
- return result;
-}
-
-bool ArticleNumber::operator<(const ArticleNumber &other) const
-{
- return parts<other.parts;
-}
-
-
-bool ArticleNumber::Part::operator<(const Part &other) const
-{
- if(number!=other.number)
- return number<other.number;
- return letter<other.letter;
-}
-
-
-void operator>>(const LexicalConverter &conv, ArticleNumber &art_nr)
-{
- art_nr = ArticleNumber(conv.get());
-}
-
-void operator<<(LexicalConverter &conv, const ArticleNumber &art_nr)
-{
- conv.result(art_nr.str());
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_ARTICLENUMBER_H_
-#define LIBMARKLIN_ARTICLENUMBER_H_
-
-#include <string>
-#include <vector>
-#include <msp/strings/lexicalcast.h>
-
-namespace Marklin {
-
-class ArticleNumber
-{
-private:
- struct Part
- {
- unsigned number;
- char letter;
-
- bool operator<(const Part &) const;
- };
-
- std::vector<Part> parts;
-
-public:
- ArticleNumber() { }
- ArticleNumber(unsigned);
- ArticleNumber(const std::string &);
-
- std::string str() const;
-
- bool operator<(const ArticleNumber &) const;
-};
-
-void operator>>(const Msp::LexicalConverter &, ArticleNumber &);
-void operator<<(Msp::LexicalConverter &, const ArticleNumber &);
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <algorithm>
-#include "block.h"
-#include "layout.h"
-#include "route.h"
-#include "trackiter.h"
-#include "tracktype.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Block::Block(Layout &l, Track &start):
- layout(l),
- id(0),
- sensor_id(start.get_sensor_id()),
- turnout_id(start.get_turnout_id()),
- train(0)
-{
- tracks.insert(&start);
- start.set_block(this);
-
- list<Track *> queue;
- queue.push_back(&start);
-
- while(!queue.empty())
- {
- Track *track = queue.front();
- queue.erase(queue.begin());
-
- const vector<Track *> &links = track->get_links();
- for(unsigned i=0; i<links.size(); ++i)
- if(links[i] && !tracks.count(links[i]))
- {
- if(links[i]->get_sensor_id()==sensor_id && links[i]->get_turnout_id()==turnout_id)
- {
- queue.push_back(links[i]);
- tracks.insert(links[i]);
- links[i]->set_block(this);
- }
- else
- endpoints.push_back(Endpoint(track, i));
- }
- }
-
- determine_id();
-
- for(unsigned i=0; i<endpoints.size(); ++i)
- {
- unsigned path = 1<<i;
- endpoints[i].paths |= path;
- find_paths(TrackIter(endpoints[i].track, endpoints[i].track_ep), path);
- }
-
- layout.add_block(*this);
-}
-
-Block::~Block()
-{
- set<Track *> trks = tracks;
- tracks.clear();
- for(set<Track *>::iterator i=trks.begin(); i!=trks.end(); ++i)
- (*i)->set_block(0);
-
- for(vector<Endpoint>::iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
- if(Block *blk = i->link)
- {
- i->link = 0;
- blk->break_link(*this);
- }
-
- layout.remove_block(*this);
-}
-
-bool Block::has_track(Track &t) const
-{
- return tracks.count(&t);
-}
-
-const Block::Endpoint &Block::get_endpoint(unsigned i) const
-{
- if(i>=endpoints.size())
- throw InvalidParameterValue("Endpoint index out of range");
-
- return endpoints[i];
-}
-
-int Block::get_endpoint_by_link(Block &other) const
-{
- for(unsigned i=0; i<endpoints.size(); ++i)
- if(endpoints[i].link==&other)
- return i;
-
- return -1;
-}
-
-float Block::get_path_length(unsigned entry, const Route *route) const
-{
- if(entry>=endpoints.size())
- throw InvalidParameterValue("Endpoint index out of range");
-
- TrackIter t_iter(endpoints[entry].track, endpoints[entry].track_ep);
-
- float result = 0;
- while(t_iter && has_track(*t_iter))
- {
- unsigned path = (route ? route->get_path(*t_iter) : t_iter->get_active_path());
- result += t_iter->get_type().get_path_length(path);
-
- t_iter = t_iter.next(path);
- }
-
- return result;
-}
-
-void Block::check_link(Block &other)
-{
- for(vector<Endpoint>::iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
- {
- if(i->link)
- continue;
-
- for(vector<Endpoint>::iterator j=other.endpoints.begin(); j!=other.endpoints.end(); ++j)
- if(j->track==i->track->get_link(i->track_ep) && j->track->get_link(j->track_ep)==i->track && !j->link)
- {
- i->link = &other;
- j->link = this;
-
- determine_id();
- other.determine_id();
- }
- }
-}
-
-void Block::break_link(Block &other)
-{
- for(vector<Endpoint>::iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
- if(i->link==&other)
- {
- i->link = 0;
- other.break_link(*this);
- determine_id();
- }
-}
-
-Block *Block::get_link(unsigned epi) const
-{
- if(epi>=endpoints.size())
- throw InvalidParameterValue("Endpoint index out of range");
- return endpoints[epi].link;
-}
-
-bool Block::reserve(Train *t)
-{
- if(!t || !train)
- {
- train = t;
- layout.signal_block_reserved.emit(*this, train);
- return true;
- }
- else
- return false;
-}
-
-void Block::find_paths(TrackIter track, unsigned path)
-{
- unsigned mask = track.endpoint().paths;
- for(unsigned i=0; mask>>i; ++i)
- if(mask&(1<<i))
- {
- TrackIter next = track.next(i);
- if(!next)
- continue;
- else if(has_track(*next))
- find_paths(track.next(i), path);
- else
- {
- next = next.flip();
- for(vector<Endpoint>::iterator j=endpoints.begin(); j!=endpoints.end(); ++j)
- if(j->track==next.track() && j->track_ep==next.entry())
- j->paths |= path;
- }
- }
-}
-
-void Block::determine_id()
-{
- if(sensor_id)
- id = 0x1000|sensor_id;
- else if(turnout_id)
- id = 0x2000|turnout_id;
- else if(endpoints.size()==2)
- {
- unsigned id1 = endpoints[0].link ? endpoints[0].link->get_id() : 1;
- unsigned id2 = endpoints[1].link ? endpoints[1].link->get_id() : 1;
- if(id2<id1)
- swap(id1, id2);
- id = (id1<<16)|id2;
- }
- else if(endpoints.size()==1)
- {
- unsigned id1 = endpoints[0].link ? endpoints[0].link->get_id() : 1;
- id = 0x10000 | id1;
- }
-}
-
-
-Block::Endpoint::Endpoint(Track *t, unsigned e):
- track(t),
- track_ep(e),
- link(0),
- paths(0)
-{ }
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_BLOCK_H_
-#define LIBMARKLIN_BLOCK_H_
-
-#include <list>
-#include <set>
-#include "track.h"
-
-namespace Marklin {
-
-class Layout;
-class Route;
-class TrackIter;
-class Train;
-
-class Block
-{
-public:
- struct Endpoint
- {
- Track *track;
- unsigned track_ep;
- Block *link;
- unsigned paths;
-
- Endpoint(Track *, unsigned);
- };
-
-private:
- Layout &layout;
- unsigned id;
- unsigned sensor_id;
- unsigned turnout_id;
- std::set<Track *> tracks;
- std::vector<Endpoint> endpoints;
- Train *train;
-
-public:
- Block(Layout &, Track &);
- ~Block();
-
- unsigned get_id() const { return id; }
- unsigned get_sensor_id() const { return sensor_id; }
- unsigned get_turnout_id() const { return turnout_id; }
- const std::set<Track *> &get_tracks() const { return tracks; }
- bool has_track(Track &) const;
- const std::vector<Endpoint> &get_endpoints() const { return endpoints; }
- const Endpoint &get_endpoint(unsigned) const;
- int get_endpoint_by_link(Block &) const;
- float get_path_length(unsigned, const Route * = 0) const;
- void check_link(Block &);
- void break_link(Block &);
- Block *get_link(unsigned) const;
- bool reserve(Train *);
- Train *get_train() const { return train; }
- void print_debug();
-private:
- void find_paths(TrackIter, unsigned);
- void determine_id();
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <msp/core/except.h>
-#include "block.h"
-#include "blockiter.h"
-#include "route.h"
-#include "trackiter.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-BlockIter::BlockIter():
- _block(0),
- _entry(0)
-{ }
-
-BlockIter::BlockIter(Block *b, unsigned e):
- _block(b),
- _entry(b ? e : 0)
-{
- if(_block && _entry>_block->get_endpoints().size())
- throw InvalidParameterValue("Endpoint index not valid for block");
-}
-
-TrackIter BlockIter::track_iter() const
-{
- if(!_block)
- return TrackIter();
-
- const Block::Endpoint &ep = _block->get_endpoint(_entry);
- return TrackIter(ep.track, ep.track_ep);
-}
-
-const Block::Endpoint &BlockIter::endpoint() const
-{
- if(!_block)
- throw InvalidState("BlockIter is null");
-
- return _block->get_endpoint(_entry);
-}
-
-int BlockIter::get_exit(const Route *route) const
-{
- const vector<Block::Endpoint> &eps = _block->get_endpoints();
- TrackIter t_iter = track_iter();
-
- while(t_iter)
- {
- if(!_block->has_track(*t_iter))
- throw LogicError("Block traversal strayed out of the block");
-
- unsigned path = (route ? route->get_path(*t_iter) : t_iter->get_active_path());
- TrackIter t_exit = t_iter.reverse(path);
-
- for(unsigned i=0; i<eps.size(); ++i)
- if(eps[i].track==t_exit.track() && eps[i].track_ep==t_exit.entry())
- return i;
-
- t_iter = t_exit.flip();
- }
-
- return -1;
-}
-
-BlockIter BlockIter::next(const Route *route) const
-{
- if(!_block)
- return BlockIter();
-
- int exit = get_exit(route);
- if(exit<0)
- return BlockIter();
-
- BlockIter result;
- result._block = _block->get_link(exit);
- result._entry = (result._block ? result._block->get_endpoint_by_link(*_block) : 0);
-
- return result;
-}
-
-BlockIter BlockIter::reverse(const Route *route) const
-{
- if(!_block)
- return BlockIter();
-
- int exit = get_exit(route);
- if(exit<0)
- return BlockIter();
-
- return BlockIter(_block, exit);
-}
-
-BlockIter BlockIter::flip() const
-{
- if(!_block)
- return BlockIter();
-
- BlockIter result;
- result._block = _block->get_link(_entry);
- result._entry = (result._block ? result._block->get_endpoint_by_link(*_block) : 0);
-
- return result;
-}
-
-Block &BlockIter::operator*() const
-{
- if(!_block)
- throw InvalidState("BlockIter is null");
-
- return *_block;
-}
-
-bool BlockIter::operator==(const BlockIter &other) const
-{
- return _block==other._block && _entry==other._entry;
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_BLOCKITER_H_
-#define LIBMARKLIN_BLOCKITER_H_
-
-namespace Marklin {
-
-class Block;
-class Route;
-class TrackIter;
-
-/**
-An iterator for traversing blocks.
-*/
-class BlockIter
-{
-private:
- Block *_block;
- unsigned _entry;
-
-public:
- BlockIter();
- BlockIter(Block *, unsigned);
-
- Block *block() const { return _block; }
- unsigned entry() const { return _entry; }
- TrackIter track_iter() const;
- const Block::Endpoint &endpoint() const;
-
-private:
- int get_exit(const Route *) const;
-public:
- BlockIter next(const Route * = 0) const;
- BlockIter reverse(const Route * = 0) const;
- BlockIter flip() const;
-
- Block &operator*() const;
- Block *operator->() const { return _block; }
- bool operator==(const BlockIter &) const;
- bool operator!=(const BlockIter &other) const { return !(*this==other); }
- operator bool() const { return _block!=0; }
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <msp/core/refptr.h>
-#include <msp/datafile/parser.h>
-#include "catalogue.h"
-#include "tracktype.h"
-#include "vehicletype.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Catalogue::Catalogue():
- scale(1),
- gauge(1.524),
- layout(*this)
-{ }
-
-Catalogue::~Catalogue()
-{
- for(TrackMap::iterator i=tracks.begin(); i!=tracks.end(); ++i)
- delete i->second;
- for(VehicleMap::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
- delete i->second;
-}
-
-float Catalogue::get_rail_elevation() const
-{
- return ballast_profile.get_height()+rail_profile.get_height();
-}
-
-void Catalogue::add_track(TrackType &track)
-{
- if(tracks.count(track.get_article_number()))
- throw Exception("Duplicate track type");
-
- tracks[track.get_article_number()] = &track;
- signal_track_added.emit(track);
-}
-
-const TrackType &Catalogue::get_track(const ArticleNumber &art_nr) const
-{
- TrackMap::const_iterator i=tracks.find(art_nr);
- if(i==tracks.end())
- throw KeyError("Unknown track type");
-
- return *i->second;
-}
-
-void Catalogue::add_vehicle(VehicleType &veh)
-{
- if(vehicles.count(veh.get_article_number()))
- throw Exception("Duplicate vehicle type");
-
- vehicles[veh.get_article_number()] = &veh;
- signal_vehicle_added.emit(veh);
-}
-
-const VehicleType &Catalogue::get_vehicle(const ArticleNumber &art_nr) const
-{
- VehicleMap::const_iterator i = vehicles.find(art_nr);
- if(i==vehicles.end())
- throw KeyError("Unknown vehicle type");
-
- return *i->second;
-}
-
-
-Catalogue::Loader::Loader(Catalogue &c):
- DataFile::BasicLoader<Catalogue>(c)
-{
- add("ballast_profile", &Loader::ballast_profile);
- add("gauge", &Loader::gauge);
- add("layout", &Loader::layout);
- add("rail_profile", &Loader::rail_profile);
- add("scale", &Loader::scale);
- add("track", static_cast<void (Loader::*)(unsigned)>(&Loader::track));
- add("track", static_cast<void (Loader::*)(ArticleNumber)>(&Loader::track));
- add("vehicle", static_cast<void (Loader::*)(unsigned)>(&Loader::vehicle));
- add("vehicle", static_cast<void (Loader::*)(ArticleNumber)>(&Loader::vehicle));
-}
-
-void Catalogue::Loader::ballast_profile()
-{
- load_sub(obj.ballast_profile);
-}
-
-void Catalogue::Loader::gauge(float g)
-{
- obj.gauge = g/1000;
- obj.path_profile = Profile();
- obj.path_profile.append_point(Point(0.1*obj.gauge, 0));
- obj.path_profile.append_point(Point(-0.1*obj.gauge, 0));
-}
-
-void Catalogue::Loader::layout()
-{
- load_sub(obj.layout);
-}
-
-void Catalogue::Loader::rail_profile()
-{
- load_sub(obj.rail_profile);
-}
-
-void Catalogue::Loader::scale(float n, float d)
-{
- obj.scale = n/d;
-}
-
-void Catalogue::Loader::track(unsigned art_nr)
-{
- track(ArticleNumber(art_nr));
-}
-
-void Catalogue::Loader::track(ArticleNumber art_nr)
-{
- if(obj.tracks.count(art_nr))
- throw KeyError("Duplicate track type", art_nr.str());
-
- RefPtr<TrackType> trk = new TrackType(art_nr);
- load_sub(*trk);
- obj.add_track(*trk.release());
-}
-
-void Catalogue::Loader::vehicle(unsigned art_nr)
-{
- vehicle(ArticleNumber(art_nr));
-}
-
-void Catalogue::Loader::vehicle(ArticleNumber art_nr)
-{
- if(obj.vehicles.count(art_nr))
- throw KeyError("Duplicate vehicle type", art_nr.str());
-
- RefPtr<VehicleType> veh = new VehicleType(art_nr);
- load_sub(*veh);
- obj.add_vehicle(*veh.release());
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_CATALOGUE_H_
-#define LIBMARKLIN_CATALOGUE_H_
-
-#include <map>
-#include <msp/datafile/loader.h>
-#include "articlenumber.h"
-#include "layout.h"
-#include "profile.h"
-
-namespace Marklin {
-
-class TrackType;
-class VehicleType;
-
-class Catalogue
-{
-public:
- class Loader: public Msp::DataFile::BasicLoader<Catalogue>
- {
- public:
- Loader(Catalogue &);
- private:
- void ballast_profile();
- void gauge(float);
- void layout();
- void rail_profile();
- void scale(float, float);
- void track(unsigned);
- void track(ArticleNumber);
- void vehicle(unsigned);
- void vehicle(ArticleNumber);
- };
-
- typedef std::map<ArticleNumber, TrackType *> TrackMap;
- typedef std::map<ArticleNumber, VehicleType *> VehicleMap;
-
- sigc::signal<void, const TrackType &> signal_track_added;
- sigc::signal<void, const VehicleType &> signal_vehicle_added;
-
-private:
- float scale;
- float gauge;
- Profile rail_profile;
- Profile ballast_profile;
- Profile path_profile;
- TrackMap tracks;
- VehicleMap vehicles;
- Layout layout;
-
-public:
- Catalogue();
- ~Catalogue();
-
- float get_scale() const { return scale; }
- float get_gauge() const { return gauge; }
- float get_rail_elevation() const;
- const Profile &get_rail_profile() const { return rail_profile; }
- const Profile &get_ballast_profile() const { return ballast_profile; }
- const Profile &get_path_profile() const { return path_profile; }
-
- void add_track(TrackType &);
- const TrackType &get_track(const ArticleNumber &) const;
- const TrackMap &get_tracks() const { return tracks; }
-
- void add_vehicle(VehicleType &);
- const VehicleType &get_vehicle(const ArticleNumber &) const;
- const VehicleMap &get_vehicles() const { return vehicles; }
-
- Layout &get_layout() { return layout; }
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <cmath>
-#include <msp/core/except.h>
-#include "controller.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-void Controller::Control::set(float v)
-{
- if(v<min_value)
- v = min_value;
- else if(v>max_value)
- v = max_value;
- else if(type==BINARY)
- value = v ? 1 : 0;
- else if(type==DISCRETE)
- value = min_value+floor((v-min_value)/step)*step;
- else if(type==CONTINUOUS)
- value = v;
-}
-
-Controller::Control Controller::Control::binary(const string &n)
-{
- Controller::Control tc;
- tc.name = n;
- tc.type = BINARY;
- tc.min_value = 0;
- tc.max_value = 1;
- tc.step = 1;
- tc.value = 0;
-
- return tc;
-}
-
-Controller::Control Controller::Control::discrete(const string &n, float m, float x, float s)
-{
- if(x<m)
- throw InvalidParameterValue("Max value must be greater than min value");
-
- Controller::Control tc;
- tc.name = n;
- tc.type = DISCRETE;
- tc.min_value = m;
- tc.max_value = m+floor((x-m)/s)*s;
- tc.step = s;
- tc.value = m;
-
- return tc;
-}
-
-Controller::Control Controller::Control::continuous(const string &n, float m, float x)
-{
- if(x<m)
- throw InvalidParameterValue("Max value must be greater than min value");
-
- Controller::Control tc;
- tc.name = n;
- tc.type = CONTINUOUS;
- tc.min_value = m;
- tc.max_value = x;
- tc.step = 0;
- tc.value = m;
-
- return tc;
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_CONTROLLER_H_
-#define LIBMARKLIN_CONTROLLER_H_
-
-#include <string>
-#include <sigc++/signal.h>
-#include <msp/time/timedelta.h>
-
-namespace Marklin {
-
-/**
-Interface class for train controllers. Takes input through a uniform named
-control interface. Provides information about train movement on output.
-*/
-class Controller
-{
-public:
- struct Control
- {
- enum Type
- {
- BINARY,
- DISCRETE,
- CONTINUOUS
- };
-
- std::string name;
- Type type;
- float min_value;
- float max_value;
- float step;
- float value;
-
- private:
- Control() { }
-
- public:
- void set(float);
-
- static Control binary(const std::string &);
- static Control discrete(const std::string &, float, float, float);
- static Control continuous(const std::string &, float, float);
- };
-
- sigc::signal<void, const Control &> signal_control_changed;
-
-protected:
- Controller() { }
-public:
- virtual ~Controller() { }
-
- virtual void set_control(const std::string &, float) = 0;
- virtual const Control &get_control(const std::string &) const = 0;
-
- /** Returns the current speed. Always non-negative. */
- virtual float get_speed() const = 0;
-
- /** Returns true if traveling in reverse. */
- virtual bool get_reverse() const = 0;
-
- /** Determines the distance required to come to a full stop. */
- virtual float get_braking_distance() const = 0;
-
- virtual void tick(const Msp::Time::TimeDelta &) = 0;
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <msp/core/except.h>
-#include "driver.h"
-#include "dummy.h"
-#include "intellibox.h"
-
-using namespace std;
-
-namespace Marklin {
-
-Driver *Driver::create(const string &str)
-{
- string::size_type colon = str.find(':');
- string type = str.substr(0, colon);
- string params;
-
- if(colon!=string::npos)
- params = str.substr(colon+1);
-
- if(type=="ib" || type=="intellibox")
- return new Intellibox(params);
- else if(type=="dummy")
- return new Dummy;
-
- throw Msp::InvalidParameterValue("Unknown driver");
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_DRIVER_H_
-#define LIBMARKLIN_DRIVER_H_
-
-#include <string>
-#include <sigc++/signal.h>
-
-namespace Marklin {
-
-class Driver
-{
-public:
- sigc::signal<void, bool> signal_power;
- sigc::signal<void, bool> signal_halt;
- sigc::signal<void, unsigned, unsigned, bool> signal_loco_speed;
- sigc::signal<void, unsigned, unsigned, bool> signal_loco_function;
- sigc::signal<void, unsigned, bool> signal_turnout;
- sigc::signal<void, unsigned, bool> signal_sensor;
-
-protected:
- Driver() { }
-public:
- virtual ~Driver() { }
-
- virtual void set_power(bool) = 0;
- virtual bool get_power() const = 0;
- virtual void halt(bool) = 0;
- virtual bool is_halted() const = 0;
-
- virtual const char *enumerate_protocols(unsigned) const = 0;
- virtual unsigned get_protocol_speed_steps(const std::string &) const = 0;
- virtual void add_loco(unsigned, const std::string &) = 0;
- virtual void set_loco_speed(unsigned, unsigned) = 0;
- virtual void set_loco_reverse(unsigned, bool) = 0;
- virtual void set_loco_function(unsigned, unsigned, bool) = 0;
-
- virtual void add_turnout(unsigned) = 0;
- virtual void set_turnout(unsigned, bool) = 0;
- virtual bool get_turnout(unsigned) const = 0;
-
- virtual void add_sensor(unsigned) = 0;
- virtual void set_sensor(unsigned, bool) = 0;
- virtual bool get_sensor(unsigned) const = 0;
-
- virtual void tick() = 0;
- virtual void flush() = 0;
-
- static Driver *create(const std::string &);
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include "dummy.h"
-
-using namespace std;
-
-namespace Marklin {
-
-Dummy::Dummy():
- power(true)
-{ }
-
-void Dummy::set_power(bool p)
-{
- power = p;
- signal_power.emit(power);
-}
-
-const char *Dummy::enumerate_protocols(unsigned i) const
-{
- if(i==0)
- return "dummy";
- return 0;
-}
-
-unsigned Dummy::get_protocol_speed_steps(const string &) const
-{
- return 0;
-}
-
-void Dummy::add_turnout(unsigned addr)
-{
- turnouts[addr];
-}
-
-void Dummy::set_turnout(unsigned addr, bool state)
-{
- if(turnouts[addr]!=state)
- {
- turnouts[addr] = state;
- signal_turnout.emit(addr, state);
- }
-}
-
-bool Dummy::get_turnout(unsigned addr) const
-{
- map<unsigned, bool>::const_iterator i = turnouts.find(addr);
- if(i!=turnouts.end())
- return i->second;
- return false;
-}
-
-void Dummy::set_loco_speed(unsigned addr, unsigned speed)
-{
- LocoState &loco = locos[addr];
- loco.speed = speed;
- signal_loco_speed.emit(addr, speed, loco.reverse);
-}
-
-void Dummy::set_loco_reverse(unsigned addr, bool rev)
-{
- LocoState &loco = locos[addr];
- loco.reverse = rev;
- signal_loco_speed.emit(addr, loco.speed, rev);
-}
-
-void Dummy::set_loco_function(unsigned addr, unsigned func, bool state)
-{
- signal_loco_function.emit(addr, func, state);
-}
-
-void Dummy::set_sensor(unsigned addr, bool state)
-{
- if(sensors[addr]!=state)
- {
- sensors[addr] = state;
- signal_sensor.emit(addr, state);
- }
-}
-
-bool Dummy::get_sensor(unsigned addr) const
-{
- map<unsigned, bool>::const_iterator i = sensors.find(addr);
- if(i!=sensors.end())
- return i->second;
- return false;
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_DUMMY_H_
-#define LIBMARKLIN_DUMMY_H_
-
-#include <map>
-#include "driver.h"
-
-namespace Marklin {
-
-class Dummy: public Driver
-{
-private:
- struct LocoState
- {
- unsigned speed;
- bool reverse;
- };
-
- bool power;
- std::map<unsigned, bool> turnouts;
- std::map<unsigned, LocoState> locos;
- std::map<unsigned, bool> sensors;
-
-public:
- Dummy();
-
- virtual void set_power(bool);
- virtual bool get_power() const { return power; }
- virtual void halt(bool) { }
- virtual bool is_halted() const { return false; }
-
- virtual const char *enumerate_protocols(unsigned) const;
- virtual unsigned get_protocol_speed_steps(const std::string &) const;
- virtual void add_loco(unsigned, const std::string &) { }
- virtual void set_loco_speed(unsigned, unsigned);
- virtual void set_loco_reverse(unsigned, bool);
- virtual void set_loco_function(unsigned, unsigned, bool);
-
- virtual void add_turnout(unsigned);
- virtual void set_turnout(unsigned, bool);
- virtual bool get_turnout(unsigned) const;
-
- virtual void add_sensor(unsigned) { }
- virtual void set_sensor(unsigned, bool);
- virtual bool get_sensor(unsigned) const;
-
- virtual void tick() { }
- virtual void flush() { }
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_GEOMETRY_H_
-#define LIBMARKLIN_GEOMETRY_H_
-
-#include <cmath>
-#include <vector>
-
-namespace Marklin {
-
-struct Point
-{
- float x, y, z;
-
- Point(): x(0), y(0), z(0) { }
- Point(float x_, float y_): x(x_), y(y_), z(0) { }
- Point(float x_, float y_, float z_): x(x_), y(y_), z(z_) { }
-};
-
-inline float distance(const Point &p, const Point &q)
-{ return sqrt((p.x-q.x)*(p.x-q.x) + (p.y-q.y)*(p.y-q.y) + (p.z-q.z)*(p.z-q.z)); }
-
-struct TrackPoint
-{
- Point pos;
- float dir;
- float grade;
-
- TrackPoint(): dir(0), grade(0) { }
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <fcntl.h>
-#include <termios.h>
-#include <sys/poll.h>
-#include <msp/io/print.h>
-#include <msp/time/units.h>
-#include <msp/time/utils.h>
-#include "intellibox.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Intellibox::Intellibox(const string &dev):
- power(false),
- halted(false),
- update_sensors(false),
- command_sent(false)
-{
- serial_fd = ::open(dev.c_str(), O_RDWR);
- if(serial_fd<0)
- throw Exception("Couldn't open serial port\n");
-
- static unsigned baud[]=
- {
- 2400, B2400,
- 4800, B4800,
- 9600, B9600,
- 19200, B19200,
- 0
- };
-
- termios attr;
- tcgetattr(serial_fd, &attr);
- cfmakeraw(&attr);
- attr.c_cflag |= CSTOPB;
-
- bool ok = false;
- bool p50 = false;
- for(unsigned i=0; baud[i]; i+=2)
- {
- cfsetospeed(&attr, baud[i+1]);
- tcsetattr(serial_fd, TCSADRAIN, &attr);
-
- write(serial_fd, "\xC4", 1);
-
- pollfd pfd = { serial_fd, POLLIN, 0 };
- if(poll(&pfd, 1, 500)>0)
- {
- IO::print("IB detected at %d bits/s\n", baud[i]);
- char buf[2];
- p50 = (read(serial_fd, buf, 2)==2);
- ok = true;
- break;
- }
- }
-
- if(!ok)
- throw Exception("IB not detected");
-
- if(p50)
- write(serial_fd, "xZzA1\r", 6);
-
- command(CMD_STATUS);
-}
-
-void Intellibox::set_power(bool p)
-{
- power = p;
- if(power)
- command(CMD_POWER_ON);
- else
- command(CMD_POWER_OFF);
- signal_power.emit(power);
-}
-
-void Intellibox::halt(bool h)
-{
- halted = h;
- if(halted)
- {
- for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
- if(i->second.speed)
- set_loco_speed(i->first, 0);
- }
-
- signal_halt.emit(halted);
-}
-
-const char *Intellibox::enumerate_protocols(unsigned i) const
-{
- if(i==MM)
- return "MM";
- else if(i==MM_27)
- return "MM-27";
- return 0;
-}
-
-unsigned Intellibox::get_protocol_speed_steps(const string &proto_name) const
-{
- Protocol proto = map_protocol(proto_name);
- if(proto==MM)
- return 14;
- else if(proto==MM_27)
- return 27;
- return 0;
-}
-
-void Intellibox::add_loco(unsigned addr, const string &proto_name)
-{
- Protocol proto = map_protocol(proto_name);
-
- if(!locos.count(addr))
- {
- locos[addr].protocol = proto;
-
- unsigned char data[2];
- data[0] = addr&0xFF;
- data[1] = (addr>>8)&0xFF;
- command(CMD_LOK_STATUS, addr, data, 2);
- }
-}
-
-void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
-{
- Locomotive &loco = locos[addr];
- if(speed==loco.speed)
- {
- if(loco.pending_half_step)
- {
- loco.pending_half_step = 0;
- loco.half_step_delay = Time::TimeStamp();
- signal_loco_speed.emit(addr, speed, loco.reverse);
- }
- return;
- }
- if(speed && halted)
- return;
-
- if(loco.protocol==MM_27)
- {
- if(speed>27)
- speed = 27;
-
- if(speed>loco.speed && !(speed&1))
- {
- loco.pending_half_step = -1;
- speed |= 1;
- }
- else if(speed<loco.speed && (speed&1))
- {
- loco.pending_half_step = 1;
- speed &= ~1;
- }
- else
- loco.pending_half_step = 0;
- loco.half_step_delay = Time::TimeStamp();
-
- loco_command(addr, (speed+1)/2, loco.reverse, loco.funcs|0x100);
- }
- else
- {
- if(speed>14)
- speed = 14;
-
- loco_command(addr, speed, loco.reverse, loco.funcs|0x100);
- }
- loco.speed = speed;
-}
-
-void Intellibox::set_loco_reverse(unsigned addr, bool rev)
-{
- Locomotive &loco = locos[addr];
- if(rev==loco.reverse)
- return;
-
- loco.reverse = rev;
- loco_command(addr, loco.speed, rev, loco.funcs|0x100);
-}
-
-void Intellibox::set_loco_function(unsigned addr, unsigned func, bool state)
-{
- Locomotive &loco = locos[addr];
- if(state)
- loco.funcs |= 1<<func;
- else
- loco.funcs &= ~(1<<func);
- loco_command(addr, loco.speed, loco.reverse, loco.funcs);
- signal_loco_function.emit(addr, func, state);
-}
-
-void Intellibox::add_turnout(unsigned addr)
-{
- if(!turnouts.count(addr))
- {
- turnouts[addr];
-
- unsigned char data[2];
- data[0] = addr&0xFF;
- data[1] = (addr>>8)&0xFF;
- command(CMD_TURNOUT_STATUS, addr, data, 2);
- }
-}
-
-void Intellibox::set_turnout(unsigned addr, bool state)
-{
- Turnout &turnout = turnouts[addr];
- if(state==turnout.state || state==turnout.pending)
- return;
-
- turnout.pending = state;
- turnout.active = true;
- turnout.off_timeout = Time::TimeStamp();
-
- turnout_command(addr, state, true);
-}
-
-bool Intellibox::get_turnout(unsigned addr) const
-{
- map<unsigned, Turnout>::const_iterator i = turnouts.find(addr);
- if(i!=turnouts.end())
- return i->second.state;
- return false;
-}
-
-void Intellibox::add_sensor(unsigned addr)
-{
- if(!sensors.count(addr))
- {
- sensors[addr];
- update_sensors = true;
- }
-}
-
-bool Intellibox::get_sensor(unsigned addr) const
-{
- map<unsigned, Sensor>::const_iterator i = sensors.find(addr);
- if(i!=sensors.end())
- return i->second.state;
- return false;
-}
-
-void Intellibox::tick()
-{
- const Time::TimeStamp t = Time::now();
-
- if(t>next_event_query)
- {
- next_event_query = t+200*Time::msec;
- command(CMD_EVENT);
- }
-
- for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
- if(i->second.protocol==MM_27 && i->second.pending_half_step && i->second.half_step_delay && t>i->second.half_step_delay)
- {
- i->second.speed += i->second.pending_half_step;
- i->second.pending_half_step = 0;
- i->second.half_step_delay = Time::TimeStamp();
- loco_command(i->first, (i->second.speed+1)/2, i->second.reverse, i->second.funcs|0x100);
- }
-
- for(map<unsigned, Turnout>::iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
- if(i->second.active && i->second.off_timeout && t>i->second.off_timeout)
- {
- i->second.active = false;
- i->second.off_timeout = Time::TimeStamp();
- turnout_command(i->first, i->second.state, false);
- }
-
- for(map<unsigned, Sensor>::iterator i=sensors.begin(); i!=sensors.end(); ++i)
- if(i->second.off_timeout && t>i->second.off_timeout)
- {
- i->second.state = false;
- i->second.off_timeout = Time::TimeStamp();
- signal_sensor.emit(i->first, false);
- }
-
- if(update_sensors)
- {
- unsigned max_addr = (--sensors.end())->first;
- unsigned char data[2];
- data[0] = 0;
- data[1] = (max_addr+7)/8;
- command(CMD_SENSOR_PARAM_SET, data, 2);
- command(CMD_SENSOR_REPORT);
- update_sensors = false;
- }
-
- if(!queue.empty() && command_sent)
- {
- pollfd pfd = { serial_fd, POLLIN, 0 };
- if(poll(&pfd, 1, 0)>0)
- {
- process_reply(t);
- queue.erase(queue.begin());
- command_sent = false;
- }
- else
- return;
- }
-
- if(!queue.empty())
- {
- const CommandSlot &slot = queue.front();
- write(serial_fd, slot.data, slot.length);
- command_sent = true;
- }
-}
-
-void Intellibox::flush()
-{
- Time::TimeStamp t = Time::now();
- for(list<CommandSlot>::iterator i=queue.begin(); i!=queue.end(); ++i)
- {
- write(serial_fd, i->data, i->length);
- pollfd pfd = { serial_fd, POLLIN, 0 };
- bool first = true;
- while(poll(&pfd, 1, (first ? -1 : 0))>0)
- {
- char data[16];
- read(serial_fd, data, 16);
- first = false;
- }
- }
-
- queue.clear();
- command_sent = false;
-}
-
-Intellibox::Protocol Intellibox::map_protocol(const string &name) const
-{
- if(name=="MM")
- return MM;
- else if(name=="MM-27")
- return MM_27;
- else
- throw InvalidParameterValue("Unknown protocol");
-}
-
-void Intellibox::command(Command cmd)
-{
- command(cmd, 0, 0);
-}
-
-void Intellibox::command(Command cmd, const unsigned char *data, unsigned len)
-{
- command(cmd, 0, data, len);
-}
-
-void Intellibox::command(Command cmd, unsigned addr, const unsigned char *data, unsigned len)
-{
- CommandSlot slot;
- slot.cmd = cmd;
- slot.addr = addr;
- slot.data[0] = cmd;
- copy(data, data+len, slot.data+1);
- slot.length = 1+len;
- queue.push_back(slot);
-}
-
-void Intellibox::loco_command(unsigned addr, unsigned speed, bool rev, unsigned funcs)
-{
- unsigned char data[4];
- data[0] = addr&0xFF;
- data[1] = (addr>>8)&0xFF;
-
- if(speed==0)
- data[2] = 0;
- else if(speed==1)
- data[2] = 2;
- else
- data[2] = (speed*19-18)/2;
-
- data[3] = (rev ? 0 : 0x20) | ((funcs&1) ? 0x10 : 0);
-
- if(!(funcs&0x100))
- data[3] |= 0x80 | ((funcs>>1)&0xF);
-
- command(CMD_LOK, addr, data, 4);
-}
-
-void Intellibox::turnout_command(unsigned addr, bool state, bool active)
-{
- unsigned char data[2];
- data[0] = addr&0xFF;
- data[1] = ((addr>>8)&0x7) | (active ? 0x40 : 0) | (state ? 0x80 : 0);
- command(CMD_TURNOUT, addr, data, 2);
-}
-
-void Intellibox::process_reply(const Time::TimeStamp &t)
-{
- Command cmd = queue.front().cmd;
-
- if(cmd==CMD_STATUS)
- {
- unsigned char status;
- read_all(&status, 1);
- power = status&0x08;
- signal_power.emit(power);
- }
- else if(cmd==CMD_EVENT)
- {
- for(unsigned i=0;; ++i)
- {
- unsigned char byte;
- read_all(&byte, 1);
-
- if(i==0)
- {
- if(byte&0x01)
- command(CMD_EVENT_LOK);
- if(byte&0x20)
- command(CMD_EVENT_TURNOUT);
- if(byte&0x04)
- command(CMD_EVENT_SENSOR);
- }
- else if(i==1)
- {
- if(byte&0x40)
- command(CMD_STATUS);
- }
-
- if(!(byte&0x80))
- break;
- }
- }
- else if(cmd==CMD_EVENT_LOK)
- {
- while(1)
- {
- unsigned char data[5];
- read_all(data, 1);
- if(data[0]==0x80)
- break;
- read_all(data+1, 4);
- }
- }
- else if(cmd==CMD_EVENT_TURNOUT)
- {
- unsigned char count;
- read_all(&count, 1);
- for(unsigned i=0; i<count; ++i)
- {
- unsigned char data[2];
- read_all(data, 2);
-
- unsigned addr = data[0]+((data[1]&7)<<8);
- Turnout &turnout = turnouts[addr];
- turnout.state = (data[1]&0x80)!=0;
- turnout.pending = turnout.state;
- signal_turnout.emit(addr, turnout.state);
- }
- }
- else if(cmd==CMD_EVENT_SENSOR)
- {
- while(1)
- {
- unsigned char mod;
- read_all(&mod, 1);
- if(!mod)
- break;
-
- unsigned char data[2];
- read_all(data, 2);
- for(unsigned i=0; i<16; ++i)
- {
- unsigned addr = mod*16+i-15;
- bool state = (data[i/8]>>(7-i%8))&1;
-
- Sensor &sensor = sensors[addr];
- if(state)
- {
- sensor.off_timeout = Time::TimeStamp();
- if(!sensor.state)
- {
- sensor.state = state;
- signal_sensor(addr, state);
- }
- }
- else if(sensor.state)
- sensor.off_timeout = t+700*Time::msec;
- }
- }
- }
- else if(cmd==CMD_LOK)
- {
- Error err;
- read_status(&err);
-
- if(err==ERR_NO_ERROR)
- {
- unsigned addr = queue.front().addr;
- Locomotive &loco = locos[addr];
- signal_loco_speed.emit(addr, loco.speed+loco.pending_half_step, loco.reverse);
- if(loco.pending_half_step)
- loco.half_step_delay = Time::now()+500*Time::msec;
- }
- else
- error(cmd, err);
- }
- else if(cmd==CMD_TURNOUT)
- {
- Error err;
- read_status(&err);
-
- unsigned addr = queue.front().addr;
- Turnout &turnout = turnouts[addr];
-
- if(err==ERR_NO_ERROR)
- {
- turnout.state = turnout.pending;
- if(turnout.active)
- {
- signal_turnout.emit(addr, turnout.state);
- turnout.off_timeout = t+500*Time::msec;
- }
- }
- else if(err==ERR_NO_I2C_SPACE)
- queue.push_back(queue.front());
- else
- {
- turnout.pending = turnout.state;
- error(cmd, err);
- }
- }
- else if(cmd==CMD_TURNOUT_STATUS)
- {
- Error err;
- read_status(&err);
-
- if(err==ERR_NO_ERROR)
- {
- unsigned char data;
- read_all(&data, 1);
-
- unsigned addr = queue.front().addr;
- bool state = data&0x04;
-
- Turnout &turnout = turnouts[addr];
- if(state!=turnout.state)
- {
- turnout.state = state;
- turnout.pending = state;
- signal_turnout.emit(addr, turnout.state);
- }
- }
- else
- error(cmd, err);
- }
- else if(cmd==CMD_LOK_STATUS)
- {
- Error err;
- read_status(&err);
-
- if(err==ERR_NO_ERROR)
- {
- unsigned char data[3];
- read_all(data, 3);
-
- unsigned addr = queue.front().addr;
- Locomotive &loco = locos[addr];
-
- unsigned speed = (data[0]<=1 ? 0 : data[0]*2/19+1);
- bool reverse = !(data[1]&0x20);
- if(speed!=loco.speed || reverse!=loco.reverse)
- {
- loco.speed = speed;
- loco.reverse = reverse;
- signal_loco_speed.emit(addr, loco.speed, loco.reverse);
- }
-
- unsigned funcs = (data[1]&0xF)<<1;
- if(data[1]&0x10)
- funcs |= 1;
- if(funcs!=loco.funcs)
- {
- unsigned changed = loco.funcs^funcs;
- loco.funcs = funcs;
- for(unsigned i=0; i<5; ++i)
- if(changed&(1<<i))
- signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
- }
- }
- else
- error(cmd, err);
- }
- else
- {
- unsigned expected_bytes = 0;
- if(cmd==CMD_FUNC_STATUS)
- expected_bytes = 1;
- if(cmd==CMD_TURNOUT_GROUP_STATUS)
- expected_bytes = 2;
- if(cmd==CMD_LOK_CONFIG)
- expected_bytes = 4;
-
- Error err;
- read_status(&err);
-
- if(err==ERR_NO_ERROR)
- {
- unsigned char data[8];
- read_all(data, expected_bytes);
- }
- else
- error(cmd, err);
- }
-}
-
-unsigned Intellibox::read_all(unsigned char *buf, unsigned len)
-{
- unsigned pos = 0;
- while(pos<len)
- pos += read(serial_fd, buf+pos, len-pos);
-
- return pos;
-}
-
-unsigned Intellibox::read_status(Error *err)
-{
- unsigned char c;
- unsigned ret = read_all(&c, 1);
- *err = static_cast<Error>(c);
- return ret;
-}
-
-void Intellibox::error(Command cmd, Error err)
-{
- const char *cmd_str = 0;
- switch(cmd)
- {
- case CMD_LOK: cmd_str = "CMD_LOK"; break;
- case CMD_LOK_STATUS: cmd_str = "CMD_LOK_STATUS"; break;
- case CMD_LOK_CONFIG: cmd_str = "CMD_LOK_CONFIG"; break;
- case CMD_FUNC: cmd_str = "CMD_FUNC"; break;
- case CMD_FUNC_STATUS: cmd_str = "CMD_FUNC_STATUS"; break;
- case CMD_TURNOUT: cmd_str = "CMD_TURNOUT"; break;
- case CMD_TURNOUT_FREE: cmd_str = "CMD_TURNOUT_FREE"; break;
- case CMD_TURNOUT_STATUS: cmd_str = "CMD_TURNOUT_STATUS"; break;
- case CMD_TURNOUT_GROUP_STATUS: cmd_str = "CMD_TURNOUT_GROUP_STATUS"; break;
- case CMD_SENSOR_STATUS: cmd_str = "CMD_SENSOR_STATUS"; break;
- case CMD_SENSOR_REPORT: cmd_str = "CMD_SENSOR_REPORT"; break;
- case CMD_SENSOR_PARAM_SET: cmd_str = "CMD_SENSOR_PARAM_SET"; break;
- case CMD_STATUS: cmd_str = "CMD_STATUS"; break;
- case CMD_POWER_OFF: cmd_str = "CMD_POWER_OFF"; break;
- case CMD_POWER_ON: cmd_str = "CMD_POWER_ON"; break;
- case CMD_NOP: cmd_str = "CMD_NOP"; break;
- case CMD_EVENT: cmd_str = "CMD_EVENT"; break;
- case CMD_EVENT_LOK: cmd_str = "CMD_EVENT_LOK"; break;
- case CMD_EVENT_TURNOUT: cmd_str = "CMD_EVENT_TURNOUT"; break;
- case CMD_EVENT_SENSOR: cmd_str = "CMD_EVENT_SENSOR"; break;
- default: cmd_str = "(unknown command)";
- }
-
- const char *err_str = 0;
- switch(err)
- {
- case ERR_NO_ERROR: err_str = "ERR_NO_ERROR"; break;
- case ERR_SYS_ERROR: err_str = "ERR_SYS_ERROR"; break;
- case ERR_BAD_PARAM: err_str = "ERR_BAD_PARAM"; break;
- case ERR_POWER_OFF: err_str = "ERR_POWER_OFF"; break;
- case ERR_NO_LOK_SPACE: err_str = "ERR_NO_LOK_SPACE"; break;
- case ERR_NO_TURNOUT_SPACE: err_str = "ERR_NO_TURNOUT_SPACE"; break;
- case ERR_NO_DATA: err_str = "ERR_NO_DATA"; break;
- case ERR_NO_SLOT: err_str = "ERR_NO_SLOT"; break;
- case ERR_BAD_LOK_ADDR: err_str = "ERR_BAD_LOK_ADDR"; break;
- case ERR_LOK_BUSY: err_str = "ERR_LOK_BUSY"; break;
- case ERR_BAD_TURNOUT_ADDR: err_str = "ERR_BAD_TURNOUT_ADDR"; break;
- case ERR_BAD_SO_VALUE: err_str = "ERR_BAD_SO_VALUE"; break;
- case ERR_NO_I2C_SPACE: err_str = "ERR_NO_I2C_SPACE"; break;
- case ERR_LOW_TURNOUT_SPACE: err_str = "ERR_LOW_TURNOUT_SPACE"; break;
- case ERR_LOK_HALTED: err_str = "ERR_LOK_HALTED"; break;
- case ERR_LOK_POWER_OFF: err_str = "ERR_LOK_POWER_OFF"; break;
- default: cmd_str = "(unknown error)";
- }
-
- IO::print("Error: %s: %s\n", cmd_str, err_str);
-}
-
-
-Intellibox::Locomotive::Locomotive():
- speed(0),
- reverse(false),
- funcs(0)
-{ }
-
-
-Intellibox::Turnout::Turnout():
- state(false),
- active(false),
- pending(false)
-{ }
-
-
-Intellibox::Sensor::Sensor():
- state(false)
-{ }
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_INTELLIBOX_H_
-#define LIBMARKLIN_INTELLIBOX_H_
-
-#include <map>
-#include <msp/time/timestamp.h>
-#include "driver.h"
-
-namespace Marklin {
-
-/**
-Driver for Uhlenbrock Intellibox. Uses the P50X binary protocol over RS232.
-
-Motorola decoders with 27 speed steps are supported by manually generating the
-commands necessary to reach the "half-steps". However, sending a rapid stream
-of speed changes to the same locomotive seems to cause excessive lag, so we
-cheat a bit; instead of sending the half-step command immediately, we send it
-with a 500ms delay, but only if no new set_loco_speed calls have occurred. As
-a downside from this accelerations and decelerations are still jerky.
-*/
-class Intellibox: public Driver
-{
-private:
- enum Command
- {
- CMD_LOK=0x80,
- CMD_LOK_STATUS=0x84,
- CMD_LOK_CONFIG=0x85,
- CMD_FUNC=0x88,
- CMD_FUNC_STATUS=0x8C,
- CMD_TURNOUT=0x90,
- CMD_TURNOUT_FREE=0x93,
- CMD_TURNOUT_STATUS=0x94,
- CMD_TURNOUT_GROUP_STATUS=0x95,
- CMD_SENSOR_STATUS=0x98,
- CMD_SENSOR_REPORT=0x99,
- CMD_SENSOR_PARAM_SET=0x9D,
- CMD_STATUS=0xA2,
- CMD_POWER_OFF=0xA6,
- CMD_POWER_ON=0xA7,
- CMD_NOP=0xC4,
- CMD_EVENT=0xC8,
- CMD_EVENT_LOK=0xC9,
- CMD_EVENT_TURNOUT=0xCA,
- CMD_EVENT_SENSOR=0xCB
- };
-
- enum Error
- {
- ERR_NO_ERROR=0,
- ERR_SYS_ERROR,
- ERR_BAD_PARAM,
- ERR_POWER_OFF=0x6,
- ERR_NO_LOK_SPACE=0x8, // No space in lok command buffer
- ERR_NO_TURNOUT_SPACE, // No space in turnout command buffer
- ERR_NO_DATA, // "no Lok status available (Lok is not in a slot)"
- ERR_NO_SLOT, // "there is no slot available"
- ERR_BAD_LOK_ADDR,
- ERR_LOK_BUSY,
- ERR_BAD_TURNOUT_ADDR,
- ERR_BAD_SO_VALUE,
- ERR_NO_I2C_SPACE,
- ERR_LOW_TURNOUT_SPACE=0x40,
- ERR_LOK_HALTED,
- ERR_LOK_POWER_OFF,
- };
-
- enum Protocol
- {
- MM,
- MM_27
- };
-
- struct Locomotive
- {
- Protocol protocol;
- unsigned speed;
- bool reverse;
- unsigned funcs;
- int pending_half_step;
- Msp::Time::TimeStamp half_step_delay;
-
- Locomotive();
- };
-
- struct Turnout
- {
- bool state;
- bool active;
- bool pending;
- Msp::Time::TimeStamp off_timeout;
-
- Turnout();
- };
-
- struct Sensor
- {
- bool state;
- Msp::Time::TimeStamp off_timeout;
-
- Sensor();
- };
-
- struct CommandSlot
- {
- Command cmd;
- unsigned addr;
- unsigned char data[8];
- unsigned length;
- };
-
- int serial_fd;
- bool power;
- bool halted;
- std::map<unsigned, Locomotive> locos;
- std::map<unsigned, Turnout> turnouts;
- std::map<unsigned, Sensor> sensors;
- bool update_sensors;
- std::list<CommandSlot> queue;
- bool command_sent;
- Msp::Time::TimeStamp next_event_query;
-
-public:
- Intellibox(const std::string &);
-
- virtual void set_power(bool);
- virtual bool get_power() const { return power; }
- virtual void halt(bool);
- virtual bool is_halted() const { return halted; }
-
- virtual const char *enumerate_protocols(unsigned) const;
- virtual unsigned get_protocol_speed_steps(const std::string &) const;
- virtual void add_loco(unsigned, const std::string &);
- virtual void set_loco_speed(unsigned, unsigned);
- virtual void set_loco_reverse(unsigned, bool);
- virtual void set_loco_function(unsigned, unsigned, bool);
-
- virtual void add_turnout(unsigned);
- virtual void set_turnout(unsigned, bool);
- virtual bool get_turnout(unsigned) const;
-
- virtual void add_sensor(unsigned);
- virtual void set_sensor(unsigned, bool) { }
- virtual bool get_sensor(unsigned) const;
-
- virtual void tick();
- virtual void flush();
-
-private:
- Protocol map_protocol(const std::string &) const;
- void command(Command);
- void command(Command, const unsigned char *, unsigned);
- void command(Command, unsigned, const unsigned char *, unsigned);
- void loco_command(unsigned, unsigned, bool, unsigned);
- void turnout_command(unsigned, bool, bool);
- void process_reply(const Msp::Time::TimeStamp &);
- unsigned read_all(unsigned char *, unsigned);
- unsigned read_status(Error *);
- void error(Command, Error);
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <algorithm>
-#include <msp/core/refptr.h>
-#include <msp/datafile/parser.h>
-#include <msp/datafile/writer.h>
-#include <msp/io/print.h>
-#include <msp/time/utils.h>
-#include "block.h"
-#include "catalogue.h"
-#include "driver.h"
-#include "layout.h"
-#include "route.h"
-#include "track.h"
-#include "tracktype.h"
-#include "train.h"
-#include "vehicletype.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Layout::Layout(Catalogue &c, Driver *d):
- catalogue(c),
- driver(d),
- next_turnout_id(0x800)
-{
- if(driver)
- driver->signal_sensor.connect(sigc::mem_fun(this, &Layout::sensor_event));
-}
-
-Layout::~Layout()
-{
- delete driver;
- while(!trains.empty())
- delete trains.begin()->second;
- while(!routes.empty())
- delete *routes.begin();
- while(!tracks.empty())
- delete *tracks.begin();
- while(!blocks.empty())
- delete *blocks.begin();
-}
-
-Driver &Layout::get_driver() const
-{
- if(!driver)
- throw InvalidState("No driver");
- return *driver;
-}
-
-void Layout::add_track(Track &t)
-{
- if(tracks.insert(&t).second)
- {
- create_blocks();
- signal_track_added.emit(t);
- }
-}
-
-void Layout::remove_track(Track &t)
-{
- if(tracks.erase(&t))
- {
- create_blocks(t);
- signal_track_removed.emit(t);
- }
-}
-
-unsigned Layout::allocate_turnout_id(bool dbl)
-{
- set<unsigned> used_ids;
- for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
- if((*i)->get_turnout_id())
- used_ids.insert((*i)->get_turnout_id());
-
- unsigned result = next_turnout_id;
- while(used_ids.count(result) || (dbl && used_ids.count(result+1)))
- ++result;
- next_turnout_id = result+1+dbl;
-
- return result;
-}
-
-void Layout::add_block(Block &b)
-{
- blocks.insert(&b);
-}
-
-Block &Layout::get_block(unsigned id) const
-{
- for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
- if((*i)->get_id()==id)
- return **i;
-
- throw KeyError("Unknown block", lexical_cast(id));
-}
-
-void Layout::create_blocks()
-{
- set<Track *> used_tracks;
- for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
- {
- const set<Track *> &btracks = (*i)->get_tracks();
- used_tracks.insert(btracks.begin(), btracks.end());
- }
-
- for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
- if(used_tracks.count(*i)==0)
- {
- Block *block = new Block(*this, **i);
- used_tracks.insert(block->get_tracks().begin(), block->get_tracks().end());
- }
-
- for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
- for(set<Block *>::iterator j=i; j!=blocks.end(); ++j)
- if(j!=i)
- (*i)->check_link(**j);
-}
-
-void Layout::create_blocks(Track &track)
-{
- /* Must collect the blocks in a set first while all tracks are still
- guaranteed to have blocks and to avoid duplicate deletes */
- set<Block *> del_blocks;
-
- del_blocks.insert(&track.get_block());
-
- const vector<Track *> &links = track.get_links();
- for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
- if(*i)
- del_blocks.insert(&(*i)->get_block());
-
- for(set<Block *>::iterator i=del_blocks.begin(); i!=del_blocks.end(); ++i)
- delete *i;
-
- create_blocks();
-}
-
-void Layout::remove_block(Block &b)
-{
- blocks.erase(&b);
-}
-
-void Layout::add_route(Route &r)
-{
- if(routes.insert(&r).second)
- signal_route_added.emit(r);
-}
-
-Route &Layout::get_route(const string &name) const
-{
- for(set<Route *>::const_iterator i=routes.begin(); i!=routes.end(); ++i)
- if((*i)->get_name()==name)
- return **i;
- throw KeyError("Unknown route", name);
-}
-
-void Layout::update_routes()
-{
- for(set<Route *>::iterator i=routes.begin(); i!=routes.end(); ++i)
- (*i)->update_turnouts();
-}
-
-void Layout::remove_route(Route &r)
-{
- if(routes.erase(&r))
- signal_route_removed.emit(r);
-}
-
-void Layout::add_train(Train &t)
-{
- if(trains.count(t.get_address()))
- throw KeyError("Duplicate train address", lexical_cast(t.get_address()));
-
- trains[t.get_address()] = &t;
- signal_train_added.emit(t);
-}
-
-Train &Layout::get_train(unsigned addr) const
-{
- map<unsigned, Train *>::const_iterator i = trains.find(addr);
- if(i==trains.end())
- throw KeyError("Unknown train", lexical_cast(addr));
- return *i->second;
-}
-
-void Layout::remove_train(Train &t)
-{
- if(trains.erase(t.get_address()))
- signal_train_removed.emit(t);
-}
-
-void Layout::add_vehicle(Vehicle &v)
-{
- if(vehicles.insert(&v).second)
- signal_vehicle_added.emit(v);
-}
-
-void Layout::remove_vehicle(Vehicle &v)
-{
- if(vehicles.erase(&v))
- signal_vehicle_removed.emit(v);
-}
-
-void Layout::tick()
-{
- if(driver)
- driver->tick();
-
- Time::TimeStamp t = Time::now();
- Time::TimeDelta dt;
- if(last_tick)
- dt = t-last_tick;
- last_tick = t;
-
- for(map<unsigned, Train *>::iterator i=trains.begin(); i!=trains.end(); ++i)
- i->second->tick(t, dt);
-}
-
-void Layout::emergency(const string &msg)
-{
- if(driver)
- driver->halt(true);
- IO::print("Emergency: %s\n", msg);
- signal_emergency.emit(msg);
-}
-
-void Layout::save(const string &fn)
-{
- IO::BufferedFile out(fn, IO::M_WRITE);
- DataFile::Writer writer(out);
-
- if(!base.empty())
- writer.write((DataFile::Statement("base"), base));
-
- for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
- {
- DataFile::Statement st("track");
- st.append((*i)->get_type().get_article_number());
- (*i)->save(st.sub);
- writer.write(st);
- }
-
- for(set<Route *>::iterator i=routes.begin(); i!=routes.end(); ++i)
- {
- if((*i)->is_temporary())
- continue;
-
- DataFile::Statement st("route");
- (*i)->save(st.sub);
- writer.write(st);
- }
-}
-
-void Layout::save_trains(const string &fn)
-{
- IO::BufferedFile out(fn, IO::M_WRITE);
- DataFile::Writer writer(out);
-
- for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
- {
- DataFile::Statement st("train");
- st.append(i->second->get_locomotive_type().get_article_number());
- st.append(i->second->get_address());
- st.append(i->second->get_protocol());
- i->second->save(st.sub);
- writer.write(st);
- }
-}
-
-void Layout::sensor_event(unsigned addr, bool state)
-{
- if(state)
- {
- for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
- if((*i)->get_sensor_id()==addr)
- {
- if(!(*i)->get_train())
- emergency(format("Unreserved sensor %d triggered", addr));
- break;
- }
- }
-}
-
-
-Layout::Loader::Loader(Layout &l):
- DataFile::BasicLoader<Layout>(l),
- new_tracks(false)
-{
- add("base", &Layout::base);
- add("route", static_cast<void (Loader::*)()>(&Loader::route));
- add("route", static_cast<void (Loader::*)(const string &)>(&Loader::route));
- add("track", static_cast<void (Loader::*)(unsigned)>(&Loader::track));
- add("track", static_cast<void (Loader::*)(ArticleNumber)>(&Loader::track));
- add("train", static_cast<void (Loader::*)(unsigned, unsigned, const std::string &)>(&Loader::train));
- add("train", static_cast<void (Loader::*)(ArticleNumber, unsigned, const std::string &)>(&Loader::train));
-}
-
-void Layout::Loader::finish()
-{
- for(set<Track *>::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i)
- (*i)->check_slope();
-}
-
-void Layout::Loader::route()
-{
- Route *rte = new Route(obj);
- load_sub(*rte);
-}
-
-void Layout::Loader::route(const string &n)
-{
- Route *rte = new Route(obj);
- rte->set_name(n);
- load_sub(*rte);
-}
-
-void Layout::Loader::track(unsigned art_nr)
-{
- track(ArticleNumber(art_nr));
-}
-
-void Layout::Loader::track(ArticleNumber art_nr)
-{
- Track *trk = new Track(obj, obj.catalogue.get_track(art_nr));
- load_sub(*trk);
- new_tracks = true;
- for(set<Track *>::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i)
- if(*i!=trk)
- trk->snap_to(**i, true);
-}
-
-void Layout::Loader::train(unsigned art_nr, unsigned addr, const std::string &proto)
-{
- train(ArticleNumber(art_nr), addr, proto);
-}
-
-void Layout::Loader::train(ArticleNumber art_nr, unsigned addr, const std::string &proto)
-{
- Train *trn = new Train(obj, obj.catalogue.get_vehicle(art_nr), addr, proto);
- load_sub(*trn);
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_LAYOUT_H_
-#define LIBMARKLIN_LAYOUT_H_
-
-#include <set>
-#include <sigc++/sigc++.h>
-#include <msp/datafile/loader.h>
-#include <msp/time/timestamp.h>
-
-namespace Marklin {
-
-class ArticleNumber;
-class Block;
-class Catalogue;
-class Driver;
-class Route;
-class Track;
-class Train;
-class Vehicle;
-
-class Layout
-{
-public:
- class Loader: public Msp::DataFile::BasicLoader<Layout>
- {
- private:
- bool new_tracks;
-
- public:
- Loader(Layout &);
- private:
- virtual void finish();
- void route();
- void route(const std::string &);
- void track(unsigned);
- void track(ArticleNumber);
- void train(unsigned, unsigned, const std::string &);
- void train(ArticleNumber, unsigned, const std::string &);
- };
-
-public:
- sigc::signal<void, Track &> signal_track_added;
- sigc::signal<void, Track &> signal_track_removed;
- sigc::signal<void, Route &> signal_route_added;
- sigc::signal<void, Route &> signal_route_removed;
- sigc::signal<void, Train &> signal_train_added;
- sigc::signal<void, Train &> signal_train_removed;
- sigc::signal<void, Vehicle &> signal_vehicle_added;
- sigc::signal<void, Vehicle &> signal_vehicle_removed;
- sigc::signal<void, Block &, Train *> signal_block_reserved;
- sigc::signal<void, const std::string &> signal_emergency;
-
-private:
- Catalogue &catalogue;
- Driver *driver;
- std::string base;
- std::set<Track *> tracks;
- std::set<Route *> routes;
- std::set<Block *> blocks;
- std::map<unsigned, Train *> trains;
- std::set<Vehicle *> vehicles;
- Msp::Time::TimeStamp last_tick;
- unsigned next_turnout_id;
-
-public:
- Layout(Catalogue &, Driver * = 0);
- ~Layout();
-
- Catalogue &get_catalogue() const { return catalogue; }
- bool has_driver() const { return driver; }
- Driver &get_driver() const;
- const std::string &get_base() const { return base; }
-
- void add_track(Track &);
- const std::set<Track *> &get_tracks() const { return tracks; }
- void remove_track(Track &);
- unsigned allocate_turnout_id(bool);
-
- void add_block(Block &);
- Block &get_block(unsigned) const;
- const std::set<Block *> &get_blocks() const { return blocks; }
- void create_blocks();
- void create_blocks(Track &);
- void remove_block(Block &);
-
- void add_route(Route &);
- const std::set<Route *> &get_routes() const { return routes; }
- Route &get_route(const std::string &) const;
- void update_routes();
- void remove_route(Route &);
-
- void add_train(Train &);
- Train &get_train(unsigned) const;
- const std::map<unsigned, Train *> &get_trains() const { return trains; }
- void remove_train(Train &);
-
- void add_vehicle(Vehicle &);
- void remove_vehicle(Vehicle &);
-
- void tick();
- void emergency(const std::string &);
-
- void save(const std::string &);
- void save_trains(const std::string &);
-private:
- void sensor_event(unsigned, bool);
-};
-
-} // namespace Marklin
-
-#endif
-
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <cmath>
-#include "profile.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-void Profile::append_point(const Point &p)
-{
- points.push_back(p);
- if(points.size()==1)
- {
- min_coords = p;
- max_coords = p;
- }
- else
- {
- min_coords.x = min(min_coords.x, p.x);
- min_coords.y = min(min_coords.y, p.y);
- max_coords.x = max(max_coords.x, p.x);
- max_coords.y = max(max_coords.y, p.y);
- }
-}
-
-const Point &Profile::get_point(unsigned i) const
-{
- if(i>=points.size())
- throw InvalidParameterValue("Index out of range");
- return points[i];
-}
-
-Point Profile::get_edge_normal(unsigned i) const
-{
- if(i+1>=points.size())
- throw InvalidParameterValue("Index out of range");
- float dx = points[i+1].x-points[i].x;
- float dy = points[i+1].y-points[i].y;
- float len = sqrt(dx*dx+dy*dy);
- return Point(dy/len, -dx/len);
-}
-
-
-Profile::Loader::Loader(Profile &p):
- DataFile::ObjectLoader<Profile>(p)
-{
- add("point", &Loader::point);
-}
-
-void Profile::Loader::point(float x, float y)
-{
- obj.append_point(Point(x/1000, y/1000));
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_PROFILE_H_
-#define LIBMARKLIN_PROFILE_H_
-
-#include <vector>
-#include <msp/datafile/objectloader.h>
-#include "geometry.h"
-
-namespace Marklin {
-
-class Profile
-{
-public:
- class Loader: public Msp::DataFile::ObjectLoader<Profile>
- {
- public:
- Loader(Profile &);
- private:
- void point(float, float);
- };
-
-private:
- std::vector<Point> points;
- Point min_coords;
- Point max_coords;
-
-public:
- void append_point(const Point &);
- unsigned get_n_points() const { return points.size(); }
- const Point &get_point(unsigned) const;
- const Point &get_min_coords() const { return min_coords; }
- const Point &get_max_coords() const { return max_coords; }
- float get_width() const { return max_coords.x-min_coords.x; }
- float get_height() const { return max_coords.y-min_coords.y; }
- Point get_edge_normal(unsigned) const;
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2007-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <queue>
-#include <msp/strings/formatter.h>
-#include "layout.h"
-#include "route.h"
-#include "track.h"
-#include "trackiter.h"
-#include "tracktype.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace {
-
-using namespace Marklin;
-
-typedef std::pair<Track *, unsigned> Key;
-
-struct Node
-{
- TrackIter track;
- Node *prev;
- float dist;
-
- Node():
- prev(0), dist(0)
- { }
-
- Node(const TrackIter &t):
- track(t), prev(0), dist(0)
- { }
-
- Node(const TrackIter &t, Node &r, float d):
- track(t), prev(&r), dist(prev->dist+d)
- { }
-
- bool operator<(const Node &other) const
- { return dist>other.dist; }
-};
-
-struct TrackMatch
-{
- Track &track;
-
- TrackMatch(Track &t): track(t) { }
-
- bool operator()(Track &t) const { return &t==&track; }
-};
-
-struct TrackInSet
-{
- const set<Track *> &tracks;
-
- TrackInSet(const set<Track *> &t): tracks(t) { }
-
- bool operator()(Track &t) const { return tracks.count(&t); }
-};
-
-template<typename Pred>
-list<Track *> dijkstra(const TrackIter &from, const Pred &goal)
-{
- map<Key, Node> track_nodes;
- priority_queue<Node> nodes;
- Node *final = 0;
-
- nodes.push(from);
-
- while(!nodes.empty())
- {
- Node lowest = nodes.top();
- nodes.pop();
-
- Key key(lowest.track.track(), lowest.track.entry());
- if(track_nodes.count(key))
- continue;
-
- Node &ref = track_nodes[key] = lowest;
- if(goal(*lowest.track))
- {
- final = &ref;
- break;
- }
-
- unsigned paths = lowest.track.endpoint().paths;
- for(unsigned i=0; paths>>i; ++i)
- if(paths&(1<<i))
- {
- TrackIter next = lowest.track.next(i);
- if(!next)
- continue;
-
- if(track_nodes.count(Key(next.track(), next.entry())))
- continue;
-
- nodes.push(Node(next, ref, lowest.track->get_type().get_path_length(i)));
- }
- }
-
- list<Track *> result;
- for(Node *node=final; node; node=node->prev)
- result.push_front(&*node->track);
-
- return result;
-}
-
-template<typename Pred>
-Route *create_route(const TrackIter &from, const Pred &goal)
-{
- list<Track *> tracks = dijkstra(from, goal);
-
- if(tracks.empty())
- return 0;
-
- Route *route = new Route(from->get_layout());
- for(list<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
- route->add_track(**i);
-
- route->set_name("Pathfinder");
- route->set_temporary(true);
-
- return route;
-}
-
-}
-
-
-namespace Marklin {
-
-Route::Route(Layout &l):
- layout(l),
- temporary(false)
-{
- layout.add_route(*this);
- layout.signal_track_removed.connect(sigc::mem_fun(this, &Route::track_removed));
-}
-
-Route::~Route()
-{
- layout.remove_route(*this);
-}
-
-void Route::set_name(const string &n)
-{
- name = n;
- signal_name_changed.emit(name);
-}
-
-void Route::set_temporary(bool t)
-{
- temporary = t;
-}
-
-void Route::set_turnout(unsigned addr, unsigned path)
-{
- if(!addr)
- throw InvalidParameterValue("Invalid turnout address");
- map<unsigned, int>::iterator i = turnouts.find(addr);
- if(i==turnouts.end())
- throw KeyError("Turnout is not in this route");
- if(i->second>=0 && path!=static_cast<unsigned>(i->second))
- throw InvalidState("Setting conflicts with route");
- i->second = path;
-}
-
-void Route::update_turnouts()
-{
- set<unsigned> found;
- for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
- if(unsigned tid = (*i)->get_turnout_id())
- {
- found.insert(tid);
-
- const vector<TrackType::Endpoint> &endpoints = (*i)->get_type().get_endpoints();
- const vector<Track *> &links = (*i)->get_links();
-
- // Build a combined path mask from linked endpoints
- unsigned mask = (*i)->get_type().get_paths();
- for(unsigned j=0; j<endpoints.size(); ++j)
- {
- if(!tracks.count(links[j]))
- continue;
-
- if(unsigned tid2 = links[j]->get_turnout_id())
- {
- const TrackType::Endpoint &ep = links[j]->get_type().get_endpoint(links[j]->get_endpoint_by_link(**i));
- int p = get_turnout(tid2);
- if(p>=0 && !(ep.paths&(1<<p)))
- {
- // The linked track is a turnout and has a path which is incompatible with this endpoint
- mask &= ~endpoints[j].paths;
- continue;
- }
- }
- mask &= endpoints[j].paths;
- }
-
- if(mask && !(mask&(mask-1)))
- {
- // Exactly one possible choice, set the path accordingly
- unsigned path = 0;
- for(; (mask && !(mask&1)); mask>>=1, ++path) ;
- turnouts[tid] = path;
- }
- else if(!turnouts.count(tid))
- // More than one possible choice, and no existing entry - set as undecided
- turnouts[tid] = -1;
- }
-
- // Remove any turnouts that do not exist in the route
- for(map<unsigned, int>::iterator i=turnouts.begin(); i!=turnouts.end();)
- {
- if(!found.count(i->first))
- turnouts.erase(i++);
- else
- ++i;
- }
-}
-
-int Route::get_turnout(unsigned id) const
-{
- map<unsigned, int>::const_iterator i = turnouts.find(id);
- if(i!=turnouts.end())
- return i->second;
- return -1;
-}
-
-unsigned Route::get_path(Track &trk) const
-{
- if(unsigned tid = trk.get_turnout_id())
- {
- map<unsigned, int>::const_iterator i = turnouts.find(tid);
- if(i!=turnouts.end())
- return i->second;
- }
- return trk.get_active_path();
-}
-
-void Route::add_track(Track &trk)
-{
- if(tracks.count(&trk))
- return;
-
- if(!tracks.empty())
- {
- unsigned valid = check_validity(trk);
- if(!(valid&1))
- throw Exception("Not linked to existing tracks");
- else if(!(valid&2))
- throw Exception("Branching routes not allowed");
- else if(!(valid&4))
- throw Exception("Route must be smooth");
- }
-
- tracks.insert(&trk);
- update_turnouts();
-}
-
-void Route::add_tracks(const set<Track *> &trks)
-{
- set<Track *> pending;
- for(set<Track *>::const_iterator i=trks.begin(); i!=trks.end(); ++i)
- if(!tracks.count(*i))
- pending.insert(*i);
-
- while(!pending.empty())
- {
- bool found = false;
- for(set<Track *>::const_iterator i=pending.begin(); i!=pending.end(); ++i)
- if(tracks.empty() || check_validity(**i)==7)
- {
- tracks.insert(*i);
- pending.erase(*i);
- found = true;
- break;
- }
-
- if(!found)
- throw Exception("Could not add all tracks to route");
- }
-
- update_turnouts();
-}
-
-void Route::add_track_chain(Track &start, unsigned ep, const TurnoutMap &trnts)
-{
- TrackIter iter(&start, ep);
- while(iter)
- {
- if(iter->get_type().is_dead_end())
- break;
-
- if(has_track(*iter))
- break;
-
- int path = 0;
- if(iter->get_turnout_id())
- {
- TurnoutMap::const_iterator i = trnts.find(iter->get_turnout_id());
- if(i==trnts.end())
- break;
-
- path = i->second;
- }
-
- add_track(*iter);
-
- iter = iter.next(path);
- }
-}
-
-bool Route::has_track(Track &t) const
-{
- return tracks.count(&t);
-}
-
-void Route::save(list<DataFile::Statement> &st) const
-{
- st.push_back((DataFile::Statement("name"), name));
- for(map<unsigned, int>::const_iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
- st.push_back((DataFile::Statement("turnout"), i->first, i->second));
-}
-
-unsigned Route::check_validity(Track &trk) const
-{
- unsigned result = 4;
- const vector<Track *> &links = trk.get_links();
- for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
- {
- if(!*i)
- continue;
- if(!tracks.count(*i))
- continue;
-
- // Linked to an existing track - good
- result |= 1;
-
- if(unsigned tid = (*i)->get_turnout_id())
- {
- const TrackType::Endpoint &ep = (*i)->get_type().get_endpoint((*i)->get_endpoint_by_link(trk));
- int path = get_turnout(tid);
- if(path>=0)
- {
- // Linking to a turnout with path set is only good if we're continuing that path
- if(ep.paths&(1<<path))
- result |= 2;
- }
- else
- {
- // Linked to a turnout with no path set - check other linked tracks
- const vector<Track *> &tlinks = (*i)->get_links();
- unsigned count = 0;
- for(unsigned j=0; j<tlinks.size(); ++j)
- if(tracks.count(tlinks[j]))
- {
- unsigned tid2 = tlinks[j]->get_turnout_id();
- if(tid2)
- {
- const TrackType::Endpoint &ep2 = tlinks[j]->get_type().get_endpoint(tlinks[j]->get_endpoint_by_link(**i));
- path = get_turnout(tid2);
- // Ignore a linked turnout with some other path set
- if(path>0 && !(ep2.paths&(1<<path)))
- continue;
- }
-
- ++count;
-
- const TrackType::Endpoint &ep2 = (*i)->get_type().get_endpoint(j);
- if(!(ep.paths&ep2.paths))
- // Impossible path through the turnout - not good
- result &= 3;
- }
-
- // Only good if at most one other track is linked to the turnout
- if(count<=1)
- result |= 2;
- }
- }
- else
- // Linked to something linear - good
- result |= 2;
- }
-
- return result;
-}
-
-void Route::track_removed(Track &t)
-{
- tracks.erase(&t);
-}
-
-Route *Route::find(const TrackIter &from, Track &to)
-{
- return create_route(from, TrackMatch(to));
-}
-
-Route *Route::find(const TrackIter &from, const Route &to)
-{
- return create_route(from, TrackInSet(to.get_tracks()));
-}
-
-Route *Route::find(const TrackIter &from, const set<Track *> &to)
-{
- return create_route(from, TrackInSet(to));
-}
-
-
-Route::Loader::Loader(Route &r):
- DataFile::BasicLoader<Route>(r)
-{
- add("name", &Route::name);
- add("turnout", &Loader::turnout);
-}
-
-void Route::Loader::finish()
-{
- const set<Track *> <racks = obj.layout.get_tracks();
- for(set<Track *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
- {
- unsigned tid = (*i)->get_turnout_id();
- if(!tid)
- continue;
-
- TurnoutMap::iterator j = turnouts.find(tid);
- if(j==turnouts.end())
- continue;
-
- unsigned path_mask = 1<<j->second;
- const vector<TrackType::Endpoint> &eps = (*i)->get_type().get_endpoints();
- for(unsigned k=0; k<eps.size(); ++k)
- if(eps[k].paths&path_mask)
- {
- Track *link = (*i)->get_link(k);
- if(!obj.tracks.count(link))
- obj.add_track_chain(*link, link->get_endpoint_by_link(**i), turnouts);
- if(!obj.tracks.count(*i))
- obj.add_track_chain(**i, k, turnouts);
- break;
- }
-
- break;
- }
-}
-
-void Route::Loader::turnout(unsigned id, unsigned path)
-{
- turnouts[id] = path;
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2007-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_ROUTE_H_
-#define LIBMARKLIN_ROUTE_H_
-
-#include <map>
-#include <set>
-#include <string>
-#include <sigc++/trackable.h>
-#include <msp/datafile/loader.h>
-
-namespace Marklin {
-
-class Layout;
-class Track;
-class TrackIter;
-
-class Route: public sigc::trackable
-{
-public:
- typedef std::map<unsigned, int> TurnoutMap;
-
- class Loader: public Msp::DataFile::BasicLoader<Route>
- {
- private:
- TurnoutMap turnouts;
-
- public:
- Loader(Route &);
- private:
- virtual void finish();
- void turnout(unsigned, unsigned);
- };
-
- sigc::signal<void, const std::string &> signal_name_changed;
-
-private:
- Layout &layout;
- std::string name;
- bool temporary;
- std::set<Track *> tracks;
- TurnoutMap turnouts;
-
-public:
- Route(Layout &);
- ~Route();
-
- void set_name(const std::string &);
- const std::string &get_name() const { return name; }
- void set_temporary(bool);
- bool is_temporary() const { return temporary; }
- void set_turnout(unsigned, unsigned);
- void update_turnouts();
- int get_turnout(unsigned) const;
- unsigned get_path(Track &) const;
- const std::map<unsigned, int> &get_turnouts() const { return turnouts; }
- void add_track(Track &);
- void add_tracks(const std::set<Track *> &);
- void add_track_chain(Track &, unsigned, const TurnoutMap &);
- const std::set<Track *> &get_tracks() const { return tracks; }
- bool has_track(Track &) const;
- void save(std::list<Msp::DataFile::Statement> &) const;
-private:
- unsigned check_validity(Track &) const;
- void track_removed(Track &);
-
-public:
- static Route *find(const TrackIter &, Track &);
- static Route *find(const TrackIter &, const Route &);
- static Route *find(const TrackIter &, const std::set<Track *> &);
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <msp/core/except.h>
-#include <msp/time/units.h>
-#include "simplecontroller.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-SimpleController::SimpleController():
- target_speed(Control::continuous("speed", 0, 1000)),
- reverse(Control::binary("reverse")),
- accel(0.07),
- speed(0)
-{
- target_speed.set(0);
-}
-
-void SimpleController::set_control(const string &name, float v)
-{
- if(name=="speed")
- {
- target_speed.set(v);
- signal_control_changed.emit(target_speed);
- }
- else if(name=="reverse")
- {
- if(target_speed.value || speed)
- throw InvalidState("Must be stopped to change reverse");
- reverse.set(v);
- signal_control_changed.emit(reverse);
- }
-}
-
-const Controller::Control &SimpleController::get_control(const string &name) const
-{
- if(name=="speed")
- return target_speed;
- else if(name=="reverse")
- return reverse;
- else
- throw KeyError("Unknown control", name);
-}
-
-float SimpleController::get_braking_distance() const
-{
- return speed*speed/(2*accel);
-}
-
-void SimpleController::tick(const Time::TimeDelta &dt)
-{
- float secs = dt/Time::sec;
- if(speed<target_speed.value)
- {
- speed += secs*accel;
- if(speed>target_speed.value)
- speed = target_speed.value;
- }
- else if(speed>target_speed.value)
- {
- speed -= secs*accel;
- if(speed<target_speed.value)
- speed = target_speed.value;
- }
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_SIMPLECONTROLLER_H_
-#define LIBMARKLIN_SIMPLECONTROLLER_H_
-
-#include <string>
-#include "controller.h"
-
-namespace Marklin {
-
-class SimpleController: public Controller
-{
-private:
- Control target_speed;
- Control reverse;
- float accel;
- float speed;
-
-public:
- SimpleController();
-
- virtual void set_control(const std::string &, float);
- virtual const Control &get_control(const std::string &) const;
-
- virtual float get_speed() const { return speed; }
- virtual bool get_reverse() const { return reverse.value; }
- virtual float get_braking_distance() const;
-
- virtual void tick(const Msp::Time::TimeDelta &);
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <msp/strings/formatter.h>
-#include <msp/time/units.h>
-#include "block.h"
-#include "catalogue.h"
-#include "driver.h"
-#include "layout.h"
-#include "timetable.h"
-#include "train.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Timetable::Timetable(Train &t):
- train(t),
- enabled(false),
- current_row(0),
- executing(true),
- pending_block(0),
- pending_train(0)
-{
- train.signal_arrived.connect(sigc::mem_fun(this, &Timetable::train_arrived));
- train.get_layout().get_driver().signal_sensor.connect(sigc::mem_fun(this, &Timetable::sensor_event));
-}
-
-void Timetable::set_enabled(bool e)
-{
- enabled = e;
-}
-
-void Timetable::reset()
-{
- current_row = 0;
- wait_timeout = Time::TimeStamp();
- pending_block = 0;
- executing = true;
-}
-
-void Timetable::clear()
-{
- rows.clear();
- reset();
-}
-
-void Timetable::append(const Row &row)
-{
- rows.push_back(row);
-}
-
-void Timetable::insert(unsigned i, const Row &row)
-{
- if(i>rows.size())
- throw InvalidParameterValue("Insert position out of range");
-
- rows.insert(rows.begin()+i, row);
- if(i<=current_row)
- ++current_row;
-}
-
-const Timetable::Row &Timetable::get_row(unsigned i) const
-{
- if(i>=rows.size())
- throw InvalidParameterValue("Row index out of range");
- return rows[i];
-}
-
-void Timetable::tick(const Time::TimeStamp &t)
-{
- if(rows.empty() || !enabled)
- return;
-
- if(wait_timeout && t>=wait_timeout)
- {
- wait_timeout = Time::TimeStamp();
- current_row = (current_row+1)%rows.size();
- executing = true;
- }
-
- if(executing)
- {
- Row &row = rows[current_row];
- switch(row.type)
- {
- case GOTO:
- if(!train.go_to(**parse_location(row.get_param<string>(0)).get_tracks().begin()))
- set_enabled(false);
- break;
- case TRAVEL:
- pending_block = &parse_location(row.get_param<string>(0));
- pending_train = &train;
- executing = false;
- break;
- case WAIT_TIME:
- wait_timeout = t+row.get_param<unsigned>(0)*Time::sec;
- executing = false;
- break;
- case WAIT_TRAIN:
- pending_train = &train.get_layout().get_train(row.get_param<unsigned>(0));
- pending_block = &parse_location(row.get_param<string>(1));
- executing = false;
- break;
- case ARRIVE:
- executing = false;
- break;
- case SPEED:
- train.set_control("speed", row.get_param<unsigned>(0)/3.6*train.get_layout().get_catalogue().get_scale());
- break;
- case REVERSE:
- train.set_control("reverse", !train.get_control("reverse"));
- break;
- case ROUTE:
- if(!train.set_route(&train.get_layout().get_route(row.get_param<string>(0))))
- set_enabled(false);
- break;
- }
-
- if(executing)
- current_row = (current_row+1)%rows.size();
- }
-}
-
-void Timetable::save(list<DataFile::Statement> &st) const
-{
- for(vector<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
- st.push_back(i->save());
-}
-
-Block &Timetable::parse_location(const string &loc)
-{
- if(!loc.compare(0, 7, "sensor "))
- return train.get_layout().get_block(lexical_cast<unsigned>(loc.substr(7))|0x1000);
- throw Exception("Named blocks are not supported yet");
-}
-
-void Timetable::sensor_event(unsigned addr, bool state)
-{
- if(pending_block && pending_block->get_train()==pending_train && addr==pending_block->get_sensor_id() && state)
- {
- pending_block = 0;
- current_row = (current_row+1)%rows.size();
- executing = true;
- }
-}
-
-void Timetable::train_arrived()
-{
- Row &row = rows[current_row];
- if(row.type==ARRIVE)
- {
- current_row = (current_row+1)%rows.size();
- executing = true;
- }
-}
-
-
-Timetable::Row::Row(RowType t):
- type(t)
-{ }
-
-template<typename T>
-Timetable::Row::Row(RowType t, const T &p):
- type(t)
-{
- params.push_back(p);
-}
-
-template<typename T>
-const T &Timetable::Row::get_param(unsigned i) const
-{
- if(i>=params.size())
- throw InvalidParameterValue("Parameter index out of range");
- return params[i].value<T>();
-}
-
-string Timetable::Row::str() const
-{
- switch(type)
- {
- case GOTO:
- return "set route to "+get_param<string>(0);
- case TRAVEL:
- return "travel to "+get_param<string>(0);
- case WAIT_TIME:
- return format("wait for %d seconds", get_param<unsigned>(0));
- case WAIT_TRAIN:
- return format("wait for train %d at %s", get_param<unsigned>(0), get_param<string>(1));
- case ARRIVE:
- return "travel until arrival";
- case SPEED:
- return format("set speed %d km/h", get_param<unsigned>(0));
- case REVERSE:
- return "reverse";
- case ROUTE:
- return "set route "+get_param<string>(0);
- default:
- return "invalid row";
- }
-}
-
-DataFile::Statement Timetable::Row::save() const
-{
- switch(type)
- {
- case GOTO:
- return DataFile::Statement("goto"), get_param<string>(0);
- case TRAVEL:
- return DataFile::Statement("travel"), get_param<string>(0);
- case WAIT_TIME:
- return DataFile::Statement("wait"), get_param<unsigned>(0);
- case WAIT_TRAIN:
- return DataFile::Statement("wait_train"), get_param<unsigned>(0), get_param<string>(1);
- case ARRIVE:
- return DataFile::Statement("arrive");
- case SPEED:
- return DataFile::Statement("speed"), get_param<unsigned>(0);
- case REVERSE:
- return DataFile::Statement("reverse");
- case ROUTE:
- return DataFile::Statement("route"), get_param<string>(0);
- default:
- return DataFile::Statement();
- }
-}
-
-Timetable::Row Timetable::Row::parse(const string &s)
-{
- if(!s.compare(0, 7, "travel "))
- {
- if(!s.compare(7, 3, "to "))
- return Row(TRAVEL, s.substr(10));
- else if(!s.compare(7, string::npos, "until arrival"))
- return Row(ARRIVE);
- }
- else if(!s.compare(0, 9, "wait for "))
- {
- if(isdigit(s[9]))
- {
- unsigned nondigit = 10;
- while(nondigit<s.size() && isdigit(s[nondigit]))
- ++nondigit;
- return Row(WAIT_TIME, lexical_cast<unsigned>(s.substr(9, nondigit-9)));
- }
- else if(!s.compare(9, 6, "train "))
- {
- string::size_type at = s.find(" at ", 15);
- if(at!=string::npos)
- {
- Row row(WAIT_TRAIN, lexical_cast<unsigned>(s.substr(15, at-15)));
- row.params.push_back(s.substr(at+4));
- return row;
- }
- }
- }
- else if(!s.compare(0, 10, "set speed "))
- {
- unsigned nondigit = 11;
- while(nondigit<s.size() && (isdigit(s[nondigit]) || s[nondigit]=='-'))
- ++nondigit;
- return Row(SPEED, lexical_cast<unsigned>(s.substr(10, nondigit-10)));
- }
- else if(s=="reverse")
- return Row(REVERSE);
- else if(!s.compare(0, 10, "set route "))
- {
- if(!s.compare(10, 3, "to "))
- return Row(GOTO, s.substr(13));
- return Row(ROUTE, s.substr(10));
- }
-
- throw InvalidParameterValue("Invalid row");
-}
-
-
-Timetable::Loader::Loader(Timetable &tt):
- DataFile::ObjectLoader<Timetable>(tt)
-{
- add("arrive", &Loader::arrive);
- add("goto", &Loader::go_to);
- add("route", &Loader::route);
- add("speed", &Loader::speed);
- add("reverse", &Loader::reverse);
- add("travel", &Loader::travel);
- add("wait", &Loader::wait);
- add("wait_train", &Loader::wait_train);
-}
-
-void Timetable::Loader::arrive()
-{
- obj.rows.push_back(Row(ARRIVE));
-}
-
-void Timetable::Loader::go_to(const string &t)
-{
- obj.rows.push_back(Row(GOTO, t));
-}
-
-void Timetable::Loader::route(const string &r)
-{
- obj.rows.push_back(Row(ROUTE, r));
-}
-
-void Timetable::Loader::reverse()
-{
- obj.rows.push_back(Row(REVERSE));
-}
-
-void Timetable::Loader::speed(unsigned s)
-{
- obj.rows.push_back(Row(SPEED, s));
-}
-
-void Timetable::Loader::travel(const string &t)
-{
- obj.rows.push_back(Row(TRAVEL, t));
-}
-
-void Timetable::Loader::wait(unsigned t)
-{
- obj.rows.push_back(Row(WAIT_TIME, t));
-}
-
-void Timetable::Loader::wait_train(unsigned t, const string &b)
-{
- Row row(WAIT_TRAIN, t);
- row.params.push_back(b);
- obj.rows.push_back(row);
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_TIMETABLE_H_
-#define LIBMARKLIN_TIMETABLE_H_
-
-#include <string>
-#include <vector>
-#include <sigc++/trackable.h>
-#include <msp/datafile/objectloader.h>
-#include <msp/time/timestamp.h>
-
-namespace Marklin {
-
-class Block;
-class Train;
-
-class Timetable: public sigc::trackable
-{
-public:
- class Loader: public Msp::DataFile::ObjectLoader<Timetable>
- {
- public:
- Loader(Timetable &);
- private:
- void arrive();
- void go_to(const std::string &);
- void route(const std::string &);
- void reverse();
- void speed(unsigned);
- void travel(const std::string &);
- void wait(unsigned);
- void wait_train(unsigned, const std::string &);
- };
-
- enum RowType
- {
- GOTO,
- TRAVEL,
- WAIT_TIME,
- WAIT_TRAIN,
- ARRIVE,
- SPEED,
- REVERSE,
- ROUTE
- };
-
- struct Row
- {
- RowType type;
- std::vector<Msp::Variant> params;
-
- Row(RowType);
-
- template<typename T>
- Row(RowType, const T &);
-
- template<typename T>
- const T &get_param(unsigned) const;
-
- std::string str() const;
-
- Msp::DataFile::Statement save() const;
-
- static Row parse(const std::string &);
- };
-
-private:
- Train &train;
- bool enabled;
- std::vector<Row> rows;
- unsigned current_row;
- bool executing;
- Block *pending_block;
- Train *pending_train;
- Msp::Time::TimeStamp wait_timeout;
-
-public:
- Timetable(Train &);
-
- void set_enabled(bool);
- bool is_enabled() const { return enabled; }
- void reset();
-
- void clear();
- void append(const Row &);
- void insert(unsigned, const Row &);
- unsigned get_n_rows() const { return rows.size(); }
- const Row &get_row(unsigned) const;
-
- void tick(const Msp::Time::TimeStamp &);
- void save(std::list<Msp::DataFile::Statement> &) const;
-private:
- Block &parse_location(const std::string &);
- void sensor_event(unsigned, bool);
- void train_arrived();
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <cmath>
-#include "block.h"
-#include "catalogue.h"
-#include "driver.h"
-#include "layout.h"
-#include "track.h"
-#include "tracktype.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Track::Track(Layout &l, const TrackType &t):
- layout(l),
- type(t),
- block(0),
- rot(0),
- slope(0),
- flex(false),
- turnout_id(type.is_turnout() ? layout.allocate_turnout_id(type.is_double_address()) : 0),
- sensor_id(0),
- links(type.get_endpoints().size()),
- active_path(0)
-{
- layout.add_track(*this);
-
- if(layout.has_driver())
- layout.get_driver().signal_turnout.connect(sigc::mem_fun(this, &Track::turnout_event));
-
- for(unsigned paths = type.get_paths(); !(paths&1); ++active_path, paths>>=1) ;
-}
-
-Track::~Track()
-{
- break_links();
- layout.remove_track(*this);
-}
-
-void Track::set_block(Block *b)
-{
- if(b && !b->has_track(*this))
- throw InvalidParameterValue("Track is not in the Block");
- if(!b && block && block->has_track(*this))
- throw InvalidState("Track is still in a Block");
-
- block = b;
-}
-
-Block &Track::get_block() const
-{
- if(!block)
- throw InvalidState("No Block");
-
- return *block;
-}
-
-void Track::set_position(const Point &p)
-{
- pos = p;
-}
-
-void Track::set_rotation(float r)
-{
- rot = r;
- while(rot<0)
- rot += M_PI*2;
- while(rot>M_PI*2)
- rot -= M_PI*2;
-}
-
-void Track::set_slope(float s)
-{
- if(links.size()!=2)
- return;
-
- slope = s;
-}
-
-void Track::set_flex(bool f)
-{
- flex = f;
-}
-
-void Track::check_slope()
-{
- if(links.size()!=2)
- return;
-
- if(links[0] && links[1])
- {
- Point epp0 = links[0]->get_endpoint_position(links[0]->get_endpoint_by_link(*this));
- Point epp1 = links[1]->get_endpoint_position(links[1]->get_endpoint_by_link(*this));
- pos.z = epp0.z;
- slope = epp1.z-pos.z;
- }
- else
- {
- slope = 0;
- if(links[0])
- {
- Point epp = links[0]->get_endpoint_position(links[0]->get_endpoint_by_link(*this));
- pos.z = epp.z;
- }
- else if(links[1])
- {
- Point epp = links[1]->get_endpoint_position(links[1]->get_endpoint_by_link(*this));
- pos.z = epp.z;
- }
- }
-}
-
-void Track::set_turnout_id(unsigned i)
-{
- if(!type.is_turnout())
- throw InvalidState("Not a turnout");
-
- turnout_id = i;
- layout.create_blocks(*this);
- layout.update_routes();
- if(layout.has_driver() && turnout_id)
- {
- layout.get_driver().add_turnout(turnout_id);
- if(type.is_double_address())
- layout.get_driver().add_turnout(turnout_id+1);
- }
-}
-
-void Track::set_sensor_id(unsigned i)
-{
- if(type.is_turnout())
- throw InvalidState("Can't set sensor on a turnout");
-
- sensor_id = i;
- layout.create_blocks(*this);
- if(layout.has_driver() && sensor_id)
- layout.get_driver().add_sensor(sensor_id);
-}
-
-void Track::set_active_path(unsigned p)
-{
- if(!turnout_id)
- throw InvalidState("Not a turnout");
- if(!(type.get_paths()&(1<<p)))
- throw InvalidParameterValue("Invalid path");
-
- layout.get_driver().set_turnout(turnout_id, p&1);
- if(type.is_double_address())
- layout.get_driver().set_turnout(turnout_id+1, p&2);
- else if(type.get_n_paths()>2)
- active_path = (active_path&1) | (p&2);
-}
-
-int Track::get_endpoint_by_link(Track &other) const
-{
- for(unsigned i=0; i<links.size(); ++i)
- if(links[i]==&other)
- return i;
-
- return -1;
-}
-
-Point Track::get_endpoint_position(unsigned epi) const
-{
- const vector<TrackType::Endpoint> &eps = type.get_endpoints();
- if(epi>=eps.size())
- throw InvalidParameterValue("TrackType::Endpoint index out of range");
-
- const TrackType::Endpoint &ep = eps[epi];
-
- float c = cos(rot);
- float s = sin(rot);
-
- Point p(pos.x+c*ep.pos.x-s*ep.pos.y, pos.y+s*ep.pos.x+c*ep.pos.y, pos.z);
- if(eps.size()==2 && epi==1)
- p.z += slope;
- return p;
-}
-
-float Track::get_endpoint_direction(unsigned epi) const
-{
- const vector<TrackType::Endpoint> &eps = type.get_endpoints();
- if(epi>=eps.size())
- throw InvalidParameterValue("TrackType::Endpoint index out of range");
-
- const TrackType::Endpoint &ep = eps[epi];
-
- return rot+ep.dir;
-}
-
-bool Track::snap_to(Track &other, bool link, float limit)
-{
- if(!limit || link)
- {
- limit = layout.get_catalogue().get_gauge();
- if(link && !flex && !other.get_flex())
- limit /= 10;
- }
- limit *= limit;
-
- const vector<TrackType::Endpoint> &eps = type.get_endpoints();
- const vector<TrackType::Endpoint> &other_eps = other.get_type().get_endpoints();
-
- for(unsigned i=0; i<eps.size(); ++i)
- {
- Point epp = get_endpoint_position(i);
-
- for(unsigned j=0; j<other_eps.size(); ++j)
- {
- if(other.get_link(j))
- continue;
-
- Point epp2 = other.get_endpoint_position(j);
- float dx = epp2.x-epp.x;
- float dy = epp2.y-epp.y;
- float dz = epp2.z-epp.z;
- if(dx*dx+dy*dy<limit && dz*dz<limit)
- {
- if(!link || (!flex && !other.get_flex()))
- {
- set_rotation(other.rot+other_eps[j].dir-eps[i].dir+M_PI);
- Point p(epp2.x-(eps[i].pos.x*cos(rot)-eps[i].pos.y*sin(rot)),
- epp2.y-(eps[i].pos.y*cos(rot)+eps[i].pos.x*sin(rot)),
- epp2.z);
- if(eps.size()==2 && i==1)
- p.z -= slope;
- set_position(p);
- }
-
- if(link)
- {
- if(links[i])
- break_link(*links[i]);
- links[i] = &other;
- other.links[j] = this;
- layout.create_blocks(*this);
- }
-
- return true;
- }
- }
- }
-
- return false;
-}
-
-bool Track::snap(Point &pt, float &d) const
-{
- const vector<TrackType::Endpoint> &eps = type.get_endpoints();
-
- for(unsigned i=0; i<eps.size(); ++i)
- {
- Point epp = get_endpoint_position(i);
- float dx = pt.x-epp.x;
- float dy = pt.y-epp.y;
- if(dx*dx+dy*dy<1e-4)
- {
- pt = epp;
- d = rot+eps[i].dir;
- return true;
- }
- }
-
- return false;
-}
-
-void Track::break_link(Track &trk)
-{
- for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
- if(*i==&trk)
- {
- *i = 0;
- trk.break_link(*this);
- // XXX Creates the blocks twice
- layout.create_blocks(*this);
- return;
- }
-}
-
-void Track::break_links()
-{
- for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
- if(Track *trk=*i)
- {
- *i = 0;
- trk->break_link(*this);
- }
-}
-
-Track *Track::get_link(unsigned i) const
-{
- if(i>links.size())
- throw InvalidParameterValue("Link index out of range");
-
- return links[i];
-}
-
-TrackPoint Track::get_point(unsigned epi, unsigned path, float d) const
-{
- TrackPoint p = type.get_point(epi, path, d);
- float c = cos(rot);
- float s = sin(rot);
-
- p.pos = Point(pos.x+c*p.pos.x-s*p.pos.y, pos.y+s*p.pos.x+c*p.pos.y, pos.z);
- p.dir += rot;
- if(type.get_endpoints().size()==2)
- {
- float len = type.get_path_length(path);
- float grade = slope/len;
- if(epi==0)
- {
- p.pos.z += grade*d;
- p.grade = grade;
- }
- else
- {
- p.pos.z += slope-grade*d;
- p.grade = -grade;
- }
- }
-
- return p;
-}
-
-TrackPoint Track::get_point(unsigned epi, float d) const
-{
- return get_point(epi, active_path, d);
-}
-
-void Track::save(list<DataFile::Statement> &st) const
-{
- st.push_back((DataFile::Statement("position"), pos.x, pos.y, pos.z));
- st.push_back((DataFile::Statement("rotation"), rot));
- st.push_back((DataFile::Statement("slope"), slope));
- if(turnout_id)
- st.push_back((DataFile::Statement("turnout_id"), turnout_id));
- if(sensor_id)
- st.push_back((DataFile::Statement("sensor_id"), sensor_id));
- if(flex)
- st.push_back((DataFile::Statement("flex"), true));
-}
-
-void Track::turnout_event(unsigned addr, bool state)
-{
- if(!turnout_id)
- return;
-
- if(addr==turnout_id)
- active_path = (active_path&2) | (state ? 1 : 0);
- else if(type.is_double_address() && addr==turnout_id+1)
- active_path = (active_path&1) | (state ? 2 : 0);
- else
- return;
-
- signal_path_changed.emit(active_path);
-}
-
-
-Track::Loader::Loader(Track &t):
- DataFile::BasicLoader<Track>(t)
-{
- add("position", &Loader::position);
- add("rotation", &Track::rot);
- add("slope", &Track::slope);
- add("turnout_id", &Loader::turnout_id);
- add("sensor_id", &Loader::sensor_id);
- add("flex", &Track::flex);
-}
-
-void Track::Loader::position(float x, float y, float z)
-{
- obj.pos = Point(x, y, z);
-}
-
-void Track::Loader::sensor_id(unsigned id)
-{
- obj.set_sensor_id(id);
-}
-
-void Track::Loader::turnout_id(unsigned id)
-{
- obj.set_turnout_id(id);
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_TRACK_H_
-#define LIBMARKLIN_TRACK_H_
-
-#include <list>
-#include <set>
-#include <sigc++/trackable.h>
-#include <msp/datafile/loader.h>
-#include "geometry.h"
-
-namespace Marklin {
-
-class Block;
-class Layout;
-class TrackType;
-
-class Track: public sigc::trackable
-{
-public:
- class Loader: public Msp::DataFile::BasicLoader<Track>
- {
- public:
- Loader(Track &);
- private:
- void position(float, float, float);
- void sensor_id(unsigned);
- void turnout_id(unsigned);
- };
-
- sigc::signal<void, unsigned> signal_path_changed;
-
-private:
- Layout &layout;
- const TrackType &type;
- Block *block;
- Point pos;
- float rot;
- float slope;
- bool flex;
- unsigned turnout_id;
- unsigned sensor_id;
- std::vector<Track *> links;
- unsigned active_path;
-
- Track(const Track &);
- Track &operator=(const Track &);
-public:
- Track(Layout &, const TrackType &);
- ~Track();
-
- Layout &get_layout() const { return layout; }
- const TrackType &get_type() const { return type; }
-
- void set_block(Block *);
- Block &get_block() const;
- void set_position(const Point &);
- void set_rotation(float);
- void set_slope(float);
- void set_flex(bool);
- const Point &get_position() const { return pos; }
- float get_rotation() const { return rot; }
- float get_slope() const { return slope; }
- bool get_flex() const { return flex; }
- void check_slope();
-
- void set_turnout_id(unsigned);
- void set_sensor_id(unsigned);
- unsigned get_turnout_id() const { return turnout_id; }
- unsigned get_sensor_id() const { return sensor_id; }
- void set_active_path(unsigned);
- unsigned get_active_path() const { return active_path; }
-
- int get_endpoint_by_link(Track &) const;
- Point get_endpoint_position(unsigned) const;
- float get_endpoint_direction(unsigned) const;
- bool snap_to(Track &, bool, float = 0);
- bool snap(Point &, float &) const;
- void break_link(Track &);
- void break_links();
- const std::vector<Track *> &get_links() const { return links; }
- Track *get_link(unsigned) const;
- TrackPoint get_point(unsigned, unsigned, float) const;
- TrackPoint get_point(unsigned, float) const;
-
- void save(std::list<Msp::DataFile::Statement> &) const;
-private:
- void turnout_event(unsigned, bool);
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <algorithm>
-#include <msp/core/except.h>
-#include "track.h"
-#include "trackiter.h"
-#include "tracktype.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-TrackIter::TrackIter():
- _track(0),
- _entry(0)
-{ }
-
-TrackIter::TrackIter(Track *t, unsigned e):
- _track(t),
- _entry(t ? e : 0)
-{
- if(_track && _entry>_track->get_type().get_endpoints().size())
- throw InvalidParameterValue("Endpoint index not valid for track");
-}
-
-const TrackType::Endpoint &TrackIter::endpoint() const
-{
- if(!_track)
- throw InvalidState("TrackIter is null");
-
- return _track->get_type().get_endpoint(_entry);
-}
-
-int TrackIter::get_exit(unsigned path) const
-{
- const vector<TrackType::Endpoint> &eps = _track->get_type().get_endpoints();
-
- // Find an endpoint that's connected to the entry and has the requested path
- for(unsigned i=0; i<eps.size(); ++i)
- if(i!=_entry && (eps[i].paths&(1<<path)) && (eps[i].paths&eps[_entry].paths))
- return i;
-
- return -1;
-}
-
-TrackIter TrackIter::next() const
-{
- if(!_track)
- return TrackIter();
-
- return next(_track->get_active_path());
-}
-
-TrackIter TrackIter::next(unsigned path) const
-{
- if(!_track)
- return TrackIter();
-
- int exit = get_exit(path);
- if(exit<0)
- return TrackIter();
-
- TrackIter result;
- result._track = _track->get_link(exit);
- result._entry = (result._track ? result._track->get_endpoint_by_link(*_track) : 0);
-
- return result;
-}
-
-TrackIter TrackIter::reverse() const
-{
- if(!_track)
- return TrackIter();
-
- return reverse(_track->get_active_path());
-}
-
-TrackIter TrackIter::reverse(unsigned path) const
-{
- if(!_track)
- return TrackIter();
-
- int exit = get_exit(path);
- if(exit<0)
- return TrackIter();
-
- return TrackIter(_track, exit);
-}
-
-TrackIter TrackIter::flip() const
-{
- if(!_track)
- return TrackIter();
-
- TrackIter result;
- result._track = _track->get_link(_entry);
- result._entry = (result._track ? result._track->get_endpoint_by_link(*_track) : 0);
-
- return result;
-}
-
-Track &TrackIter::operator*() const
-{
- if(!_track)
- throw InvalidState("TrackIter is null");
-
- return *_track;
-}
-
-bool TrackIter::operator==(const TrackIter &other) const
-{
- return _track==other._track && _entry==other._entry;
-}
-
-
-TrackLoopIter::TrackLoopIter():
- _looped(false)
-{ }
-
-TrackLoopIter::TrackLoopIter(Track *t, unsigned e):
- TrackIter(t, e),
- _visited(new TrackList()),
- _last(_visited->insert(_visited->end(), track())),
- _looped(false)
-{ }
-
-TrackLoopIter::TrackLoopIter(const TrackIter &i):
- TrackIter(i),
- _visited(new TrackList()),
- _last(_visited->insert(_visited->end(), track())),
- _looped(false)
-{ }
-
-TrackLoopIter::TrackLoopIter(const TrackIter &i, RefPtr<TrackList> v, const TrackList::iterator &l):
- TrackIter(i),
- _looped(false)
-{
- if(track())
- {
- _visited = v;
- _last = l;
- _looped = (_visited && find(_visited->begin(), _last, track())!=_last);
-
- ++_last;
- if(_last!=_visited->end())
- {
- _visited = new TrackList(_visited->begin(), _last);
- _last = _visited->end();
- }
- _visited->push_back(track());
- --_last;
- }
-}
-
-TrackLoopIter TrackLoopIter::next() const
-{
- return TrackLoopIter(TrackIter::next(), _visited, _last);
-}
-
-TrackLoopIter TrackLoopIter::next(unsigned path) const
-{
- return TrackLoopIter(TrackIter::next(path), _visited, _last);
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_TRACKITER_H_
-#define LIBMARKLIN_TRACKITER_H_
-
-#include <set>
-#include <msp/core/refptr.h>
-#include "tracktype.h"
-
-namespace Marklin {
-
-class Track;
-
-/**
-An iterator for traversing tracks.
-*/
-class TrackIter
-{
-private:
- Track *_track;
- unsigned _entry;
-
-public:
- TrackIter();
- TrackIter(Track *, unsigned);
-
- Track *track() const { return _track; }
- unsigned entry() const { return _entry; }
- const TrackType::Endpoint &endpoint() const;
-
-private:
- int get_exit(unsigned) const;
-public:
- TrackIter next() const;
- TrackIter next(unsigned) const;
- TrackIter reverse() const;
- TrackIter reverse(unsigned) const;
- TrackIter flip() const;
-
- Track &operator*() const;
- Track *operator->() const { return _track; }
- bool operator==(const TrackIter &) const;
- bool operator!=(const TrackIter &other) const { return !(*this==other); }
- operator bool() const { return _track!=0; }
-};
-
-
-/**
-A track iterator that detects looping.
-
-A list of visited tracks is maintained internally to the iterator. This list
-is shared between iterators as long as next() is only called once per iterator.
-Subsequent calls to next() cause the head of the list to be copied.
-*/
-class TrackLoopIter: public TrackIter
-{
-private:
- typedef std::list<Track *> TrackList;
-
- Msp::RefPtr<TrackList> _visited;
- TrackList::iterator _last;
- bool _looped;
-
-public:
- TrackLoopIter();
- TrackLoopIter(Track *, unsigned);
- TrackLoopIter(const TrackIter &);
-private:
- TrackLoopIter(const TrackIter &, Msp::RefPtr<TrackList>, const TrackList::iterator &);
-
-public:
- bool looped() const { return _looped; }
-
- TrackLoopIter next() const;
- TrackLoopIter next(unsigned) const;
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <cmath>
-#include "trackpart.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-TrackPart::TrackPart():
- dir(0),
- length(0),
- radius(0),
- path(0),
- dead_end(false)
-{
- links[0] = 0;
- links[1] = 0;
-}
-
-float TrackPart::get_length() const
-{
- if(radius)
- return abs(radius)*length;
- else
- return length;
-}
-
-TrackPoint TrackPart::get_point(float d) const
-{
- TrackPoint result;
-
- if(radius)
- {
- float a = d/radius;
- float c = cos(a);
- float s = sin(a);
- float rx = radius*sin(dir);
- float ry = -radius*cos(dir);
- result.pos = Point(pos.x+c*rx-s*ry-rx, pos.y+c*ry+s*rx-ry);
- result.dir = dir+a;
- }
- else
- {
- result.pos = Point(pos.x+cos(dir)*d, pos.y+sin(dir)*d);
- result.dir = dir;
- }
-
- return result;
-}
-
-void TrackPart::check_link(TrackPart &other)
-{
- unsigned n_eps = (dead_end ? 1 : 2);
- unsigned n_other_eps = (other.is_dead_end() ? 1 : 2);
- for(unsigned i=0; i<n_eps; ++i)
- {
- TrackPoint p1 = get_point(i ? get_length() : 0);
- for(unsigned j=0; j<n_other_eps; ++j)
- {
- TrackPoint p2 = other.get_point(j ? other.get_length() : 0);
-
- float dx = p2.pos.x-p1.pos.x;
- float dy = p2.pos.y-p1.pos.y;
-
- float da = p2.dir-p1.dir+M_PI*((i+j+1)%2);
- while(da>M_PI)
- da -= M_PI*2;
- while(da<-M_PI)
- da += M_PI*2;
-
- if(dx*dx+dy*dy<1e-6 && da>=-0.01 && da<=0.01)
- {
- links[i] = &other;
- other.links[j] = this;
- return;
- }
- }
- }
-}
-
-TrackPart *TrackPart::get_link(unsigned i) const
-{
- if(i>=2)
- throw InvalidParameterValue("Index out of range");
- return links[i];
-}
-
-
-TrackPart::Loader::Loader(TrackPart &p):
- Msp::DataFile::BasicLoader<TrackPart>(p)
-{
- add("start", &Loader::start);
- add("length", &TrackPart::length);
- add("radius", &TrackPart::radius);
- add("path", &TrackPart::path);
- add("dead_end", &TrackPart::dead_end);
-}
-
-void TrackPart::Loader::finish()
-{
- if(obj.radius)
- {
- obj.length *= M_PI/180;
- obj.radius /= 1000;
- }
- else
- obj.length /= 1000;
-
- obj.pos.x /= 1000;
- obj.pos.y /= 1000;
- obj.dir *= M_PI/180;
-}
-
-void TrackPart::Loader::start(float x, float y, float d)
-{
- obj.pos = Point(x, y);
- obj.dir = d;
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef MARKLIN_TRACKPART_H_
-#define MARKLIN_TRACKPART_H_
-
-#include <msp/datafile/loader.h>
-#include "geometry.h"
-
-namespace Marklin {
-
-class TrackPart
-{
-public:
- class Loader: public Msp::DataFile::BasicLoader<TrackPart>
- {
- public:
- Loader(TrackPart &);
- private:
- virtual void finish();
- void start(float, float, float);
- };
-
-private:
- Point pos;
- float dir;
- float length;
- float radius;
- unsigned path;
- bool dead_end;
- TrackPart *links[2];
-
-public:
- TrackPart();
-
- float get_length() const;
- bool is_curved() const { return radius; }
- TrackPoint get_point(float) const;
- unsigned get_path() const { return path; }
- bool is_dead_end() const { return dead_end; }
- void check_link(TrackPart &);
- TrackPart *get_link(unsigned) const;
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <cmath>
-#include "tracktype.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-TrackType::TrackType(const ArticleNumber &an):
- art_nr(an),
- double_address(false),
- autofit_preference(1)
-{ }
-
-float TrackType::get_total_length() const
-{
- return get_path_length(-1);
-}
-
-float TrackType::get_path_length(int p) const
-{
- float len = 0;
- for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
- if(p<0 || i->get_path()==static_cast<unsigned>(p))
- len += i->get_length();
- return len;
-}
-
-unsigned TrackType::get_paths() const
-{
- unsigned mask = 0;
- for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
- mask |= 1<<i->get_path();
- return mask;
-}
-
-unsigned TrackType::get_n_paths() const
-{
- unsigned n = 0;
- for(unsigned mask = get_paths(); mask; ++n)
- mask &= mask-1;
- return n;
-}
-
-bool TrackType::is_turnout() const
-{
- return endpoints.size()>2;
-}
-
-bool TrackType::is_dead_end() const
-{
- return endpoints.size()<2;
-}
-
-const TrackType::Endpoint &TrackType::get_endpoint(unsigned i) const
-{
- if(i>=endpoints.size())
- throw InvalidParameterValue("Endpoint index out of range");
-
- return endpoints[i];
-}
-
-TrackPoint TrackType::get_point(unsigned epi, unsigned path, float d) const
-{
- if(epi>=endpoints.size())
- throw InvalidParameterValue("Endpoint index out of range");
-
- const TrackPart *part = 0;
- unsigned part_ep = 0;
- for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
- {
- if((endpoints[epi].paths&(1<<path)) && i->get_path()!=path)
- continue;
-
- unsigned n_part_eps = (i->is_dead_end() ? 1 : 2);
- for(unsigned j=0; j<n_part_eps; ++j)
- {
- TrackPoint p = i->get_point(j ? i->get_length() : 0);
- float dx = p.pos.x-endpoints[epi].pos.x;
- float dy = p.pos.y-endpoints[epi].pos.y;
- if(dx*dx+dy*dy<1e-6)
- {
- part = &*i;
- part_ep = j;
- }
- }
- }
-
- if(!part)
- throw Exception("Internal error (endpoint does not match any part)");
-
- while(1)
- {
- float plen = part->get_length();
- if(d<=plen)
- {
- if(part_ep==1)
- d = plen-d;
- TrackPoint p = part->get_point(d);
- if(part_ep==1)
- p.dir += M_PI;
- return p;
- }
- else
- {
- d -= plen;
- TrackPart *next = part->get_link(1-part_ep);
- if(!next)
- throw InvalidParameterValue("Distance out of range");
- part_ep = (next->get_link(0)==part ? 0 : 1);
- part = next;
- }
- }
-}
-
-void TrackType::collect_endpoints()
-{
- endpoints.clear();
-
- for(vector<TrackPart>::iterator i=parts.begin(); i!=parts.end(); ++i)
- {
- for(vector<TrackPart>::iterator j=i; ++j!=parts.end();)
- i->check_link(*j);
-
- unsigned n_part_eps = (i->is_dead_end() ? 1 : 2);
- for(unsigned j=0; j<n_part_eps; ++j)
- if(!i->get_link(j))
- {
- TrackPoint p = i->get_point(j ? i->get_length() : 0);
- if(j==0)
- p.dir += M_PI;
-
- bool found = false;
- for(vector<Endpoint>::iterator k=endpoints.begin(); k!=endpoints.end(); ++k)
- {
- float dx = k->pos.x-p.pos.x;
- float dy = k->pos.y-p.pos.y;
-
- float da = k->dir-p.dir;
- while(da>M_PI)
- da -= M_PI*2;
- while(da<-M_PI)
- da += M_PI*2;
-
- if(dx*dx+dy*dy<1e-6 && da>-0.01 && da<0.01)
- {
- k->paths |= 1<<i->get_path();
- found = true;
- break;
- }
- }
-
- if(!found)
- endpoints.push_back(Endpoint(p.pos.x, p.pos.y, p.dir, 1<<i->get_path()));
- }
- }
-}
-
-TrackType::Endpoint::Endpoint(float x, float y, float d, unsigned p):
- pos(x, y),
- dir(d),
- paths(p)
-{ }
-
-
-TrackType::Loader::Loader(TrackType &t):
- Msp::DataFile::BasicLoader<TrackType>(t)
-{
- add("autofit_preference", &TrackType::autofit_preference);
- add("description", &TrackType::description);
- add("double_address", &TrackType::double_address);
- add("part", &Loader::part);
-}
-
-void TrackType::Loader::finish()
-{
- obj.collect_endpoints();
-}
-
-void TrackType::Loader::part()
-{
- TrackPart p;
- load_sub(p);
- obj.parts.push_back(p);
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_TRACKTYPE_H_
-#define LIBMARKLIN_TRACKTYPE_H_
-
-#include <msp/datafile/loader.h>
-#include "articlenumber.h"
-#include "geometry.h"
-#include "trackpart.h"
-
-namespace Marklin {
-
-class TrackType
-{
-public:
- struct Endpoint
- {
- Point pos;
- float dir; // Direction outwards from the endpoint
- unsigned paths;
-
- Endpoint(float, float, float, unsigned);
- };
-
- class Loader: public Msp::DataFile::BasicLoader<TrackType>
- {
- public:
- Loader(TrackType &);
- private:
- virtual void finish();
- void part();
- void position(float, float, float);
- };
-
-private:
- ArticleNumber art_nr;
- std::string description;
- std::vector<TrackPart> parts;
- std::vector<Endpoint> endpoints;
- bool double_address;
- unsigned autofit_preference;
-
-public:
- TrackType(const ArticleNumber &);
-
- const ArticleNumber &get_article_number() const { return art_nr; }
- const std::string &get_description() const { return description; }
- float get_total_length() const;
- float get_path_length(int) const;
- unsigned get_paths() const;
- unsigned get_n_paths() const;
- bool is_turnout() const;
- bool is_dead_end() const;
- bool is_double_address() const { return double_address; }
- unsigned get_autofit_preference() const { return autofit_preference; }
- const std::vector<TrackPart> &get_parts() const { return parts; }
- const std::vector<Endpoint> &get_endpoints() const { return endpoints; }
- const Endpoint &get_endpoint(unsigned) const;
- TrackPoint get_point(unsigned, unsigned, float) const;
-
-private:
- void collect_endpoints();
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <cmath>
-#include <msp/strings/formatter.h>
-#include <msp/time/units.h>
-#include <msp/time/utils.h>
-#include "aicontrol.h"
-#include "catalogue.h"
-#include "driver.h"
-#include "layout.h"
-#include "route.h"
-#include "simplecontroller.h"
-#include "timetable.h"
-#include "trackiter.h"
-#include "tracktype.h"
-#include "train.h"
-#include "vehicle.h"
-#include "vehicletype.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace {
-
-struct SetFlag
-{
- bool &flag;
-
- SetFlag(bool &f): flag(f) { flag = true; }
- ~SetFlag() { flag = false; }
-};
-
-}
-
-
-namespace Marklin {
-
-Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
- layout(l),
- loco_type(t),
- address(a),
- protocol(p),
- priority(0),
- yielding_to(0),
- cur_blocks_end(blocks.end()),
- clear_blocks_end(blocks.end()),
- pending_block(0),
- reserving(false),
- advancing(false),
- controller(new AIControl(*this, new SimpleController)),
- timetable(0),
- active(false),
- current_speed_step(0),
- speed_changing(false),
- reverse(false),
- functions(0),
- end_of_route(false),
- status("Unplaced"),
- travel_dist(0),
- pure_speed(false),
- real_speed(layout.get_driver().get_protocol_speed_steps(protocol)+1),
- accurate_position(false),
- overshoot_dist(false)
-{
- if(!loco_type.is_locomotive())
- throw InvalidParameterValue("Initial vehicle must be a locomotive");
-
- vehicles.push_back(new Vehicle(layout, loco_type));
-
- layout.add_train(*this);
-
- layout.get_driver().add_loco(address, protocol);
- layout.get_driver().signal_loco_speed.connect(sigc::mem_fun(this, &Train::loco_speed_event));
- layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event));
-
- layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
- layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Train::sensor_event));
-
- layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
-
- const set<Track *> &tracks = layout.get_tracks();
- for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
- if((*i)->get_turnout_id())
- (*i)->signal_path_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changed), sigc::ref(**i))));
-
- controller->signal_control_changed.connect(sigc::mem_fun(this, &Train::control_changed));
-}
-
-Train::~Train()
-{
- delete controller;
- delete timetable;
- for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
- delete *i;
- layout.remove_train(*this);
-}
-
-void Train::set_name(const string &n)
-{
- name = n;
-
- signal_name_changed.emit(name);
-}
-
-void Train::set_priority(int p)
-{
- priority = p;
-}
-
-void Train::yield_to(const Train &t)
-{
- yielding_to = &t;
-}
-
-void Train::add_vehicle(const VehicleType &vt)
-{
- Vehicle *veh = new Vehicle(layout, vt);
- vehicles.back()->attach_back(*veh);
- vehicles.push_back(veh);
-}
-
-void Train::remove_vehicle(unsigned i)
-{
- if(i>=vehicles.size())
- throw InvalidParameterValue("Vehicle index out of range");
- if(i==0)
- throw InvalidParameterValue("Can't remove the locomotive");
- delete vehicles[i];
- vehicles.erase(vehicles.begin()+i);
- if(i<vehicles.size())
- vehicles[i-1]->attach_back(*vehicles[i]);
-}
-
-unsigned Train::get_n_vehicles() const
-{
- return vehicles.size();
-}
-
-Vehicle &Train::get_vehicle(unsigned i)
-{
- if(i>=vehicles.size())
- throw InvalidParameterValue("Vehicle index out of range");
- return *vehicles[i];
-}
-
-const Vehicle &Train::get_vehicle(unsigned i) const
-{
- if(i>=vehicles.size())
- throw InvalidParameterValue("Vehicle index out of range");
- return *vehicles[i];
-}
-
-void Train::set_control(const string &n, float v)
-{
- controller->set_control(n, v);
-}
-
-void Train::set_active(bool a)
-{
- if(a==active)
- return;
- if(!a && controller->get_speed())
- throw InvalidState("Can't deactivate while moving");
-
- active = a;
- if(active)
- {
- stop_timeout = Time::TimeStamp();
- reserve_more();
- }
- else
- {
- stop_timeout = Time::now()+2*Time::sec;
- set_status("Stopped");
- }
-}
-
-void Train::set_function(unsigned func, bool state)
-{
- if(!loco_type.get_functions().count(func))
- throw InvalidParameterValue("Invalid function");
- if(func<5)
- layout.get_driver().set_loco_function(address, func, state);
- else
- layout.get_driver().set_loco_function(address+1, func-4, state);
-}
-
-float Train::get_control(const string &ctrl) const
-{
- return controller->get_control(ctrl).value;
-}
-
-float Train::get_speed() const
-{
- return controller->get_speed();
-}
-
-bool Train::get_function(unsigned func) const
-{
- return (functions>>func)&1;
-}
-
-void Train::set_timetable(Timetable *tt)
-{
- delete timetable;
- timetable = tt;
-}
-
-bool Train::set_route(const Route *r)
-{
- free_noncritical_blocks();
-
- Route *lead = 0;
- if(r && !blocks.empty())
- {
- TrackIter first = blocks.front().track_iter();
- TrackIter next = blocks.back().next().track_iter();
- if(!r->has_track(*next))
- {
- lead = Route::find(next, *r);
- if(!lead)
- return false;
- create_lead_route(lead, lead);
- routes.push_front(lead);
- }
- else if(!r->has_track(*first))
- lead = create_lead_route(0, r);
- }
-
- routes.clear();
- if(lead)
- routes.push_back(lead);
- if(r)
- routes.push_back(r);
- end_of_route = false;
-
- reserve_more();
-
- signal_route_changed.emit(get_route());
-
- return true;
-}
-
-bool Train::go_to(Track &to)
-{
- for(BlockList::const_iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
- if((*i)->has_track(to))
- {
- signal_arrived.emit();
- return set_route(0);
- }
-
- free_noncritical_blocks();
-
- TrackIter next = blocks.back().next().track_iter();
-
- Route *route = Route::find(next, to);
- if(!route)
- return false;
- create_lead_route(route, route);
- return set_route(route);
-}
-
-bool Train::divert(Track &from)
-{
- if(!from.get_turnout_id())
- throw InvalidParameterValue("Can't divert from a non-turnout");
- if(routes.empty())
- return false;
-
- unsigned path = 0;
- unsigned entry = 0;
- list<RouteRef>::iterator route = routes.begin();
-
- // Follow our routes to find out where we're entering the turnout
- for(TrackLoopIter track = blocks.front().track_iter();;)
- {
- if(!advance_route(route, *track))
- return false;
-
- if(&*track==&from)
- {
- Block &block = track->get_block();
- if(block.get_train()==this && !free_block(block))
- return false;
-
- int route_path = route->route->get_turnout(from.get_turnout_id());
-
- // Check that more than one path is available
- unsigned ep_paths = track.endpoint().paths;
- if(!(ep_paths&(ep_paths-1)))
- return false;
-
- // Choose some other path
- for(int i=0; ep_paths>>i; ++i)
- if((ep_paths&(1<<i)) && i!=route_path)
- {
- path = i;
- break;
- }
-
- entry = track.entry();
- break;
- }
-
- track = track.next(route->route->get_path(*track));
-
- if(!track || track.looped())
- return false;
- }
-
- TrackIter track = TrackIter(&from, entry).next(path);
- if(!track)
- return false;
-
- set<Track *> tracks;
- for(list<RouteRef>::iterator i=routes.begin(); i!=routes.end(); ++i)
- tracks.insert(i->route->get_tracks().begin(), i->route->get_tracks().end());
- RefPtr<Route> diversion = Route::find(track, tracks);
- if(!diversion)
- return false;
-
- diversion->set_name("Diversion");
- diversion->add_track(from);
- diversion->set_turnout(from.get_turnout_id(), path);
-
- if(!is_valid_diversion(*diversion, TrackIter(&from, entry)))
- return false;
-
- // Follow the diversion route until we get back to the original route
- list<RouteRef>::iterator end = routes.end();
- while(1)
- {
- for(list<RouteRef>::iterator i=route; (end==routes.end() && i!=routes.end()); ++i)
- if(i->route->has_track(*track))
- end = i;
-
- if(end!=routes.end())
- break;
- else if(!diversion->has_track(*track))
- throw LogicError("Pathfinder returned a bad route");
-
- track = track.next(diversion->get_path(*track));
- }
-
- if(route==end)
- // We are rejoining the same route we diverted from, duplicate it
- routes.insert(end, *route);
- else
- {
- ++route;
- routes.erase(route, end);
- }
- routes.insert(end, RouteRef(diversion.release(), from.get_turnout_id()));
-
- return true;
-}
-
-const Route *Train::get_route() const
-{
- if(routes.empty())
- return 0;
- return routes.front().route;
-}
-
-void Train::place(Block &block, unsigned entry)
-{
- if(controller->get_speed())
- throw InvalidState("Must be stopped before placing");
-
- release_blocks();
-
- set_active(false);
- accurate_position = false;
-
- if(!block.reserve(this))
- {
- set_status("Unplaced");
- return;
- }
-
- blocks.push_back(BlockIter(&block, entry));
- if(reverse)
- {
- TrackIter track = BlockIter(&block, entry).reverse().track_iter();
- vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_BUFFER);
- }
- else
- {
- const Block::Endpoint &bep = block.get_endpoint(entry);
- vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
- }
-}
-
-void Train::unplace()
-{
- if(controller->get_speed())
- throw InvalidState("Must be stopped before unplacing");
-
- release_blocks();
-
- set_active(false);
- accurate_position = false;
-
- for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
- (*i)->unplace();
-
- set_status("Unplaced");
-}
-
-bool Train::free_block(Block &block)
-{
- float margin = 10*layout.get_catalogue().get_scale();
- if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3+margin)
- return false;
-
- unsigned nsens = 0;
- for(BlockList::iterator i=cur_blocks_end; i!=blocks.end(); ++i)
- {
- if(i->block()==&block)
- {
- if(nsens<1)
- return false;
- release_blocks(i, blocks.end());
- return true;
- }
- else if((*i)->get_sensor_id())
- ++nsens;
- }
-
- return false;
-}
-
-void Train::free_noncritical_blocks()
-{
- if(blocks.empty())
- return;
-
- if(controller->get_speed()==0)
- {
- release_blocks(cur_blocks_end, blocks.end());
- return;
- }
-
- float margin = 10*layout.get_catalogue().get_scale();
- float min_dist = controller->get_braking_distance()*1.3+margin;
-
- Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
-
- TrackIter track(veh.get_track(), veh.get_entry());
- BlockList::iterator block = blocks.begin();
- bool in_rsv = false;
- while(block!=blocks.end() && !(*block)->has_track(*track))
- {
- ++block;
- if(block==cur_blocks_end)
- in_rsv = true;
- }
-
- float dist = veh.get_offset();
- if(reverse)
- track.reverse();
- else
- dist = track->get_type().get_path_length(track->get_active_path())-dist;
- dist -= veh.get_type().get_length()/2;
-
- bool nsens = 0;
- while(1)
- {
- track = track.next();
-
- if(!(*block)->has_track(*track))
- {
- ++block;
- if(block==cur_blocks_end)
- in_rsv = true;
- if(block==blocks.end())
- return;
-
- if(dist>min_dist && nsens>0)
- {
- release_blocks(block, blocks.end());
- return;
- }
-
- if(in_rsv && (*block)->get_sensor_id())
- ++nsens;
- }
-
- dist += track->get_type().get_path_length(track->get_active_path());
- }
-}
-
-int Train::get_entry_to_block(Block &block) const
-{
- for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
- if(i->block()==&block)
- return i->entry();
- return -1;
-}
-
-float Train::get_reserved_distance() const
-{
- return get_reserved_distance_until(0, false);
-}
-
-void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
-{
- if(!active && stop_timeout && t>=stop_timeout)
- {
- release_blocks(cur_blocks_end, blocks.end());
- stop_timeout = Time::TimeStamp();
- }
-
- Driver &driver = layout.get_driver();
-
- if(timetable)
- timetable->tick(t);
- controller->tick(dt);
- float speed = controller->get_speed();
- unsigned speed_step = find_speed_step(speed);
-
- if(controller->get_reverse()!=reverse)
- {
- reverse = controller->get_reverse();
- driver.set_loco_reverse(address, reverse);
-
- release_blocks(cur_blocks_end, blocks.end());
- reverse_blocks(blocks);
-
- reserve_more();
- }
- if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
- {
- speed_changing = true;
- driver.set_loco_speed(address, speed_step);
-
- pure_speed = false;
-
- if(speed_step)
- set_status(format("Traveling %d kmh", get_travel_speed()));
- else
- set_status("Waiting");
- }
-
- if(speed)
- {
- if(!active)
- set_active(true);
-
- Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
- Track *track = vehicle.get_track();
-
- bool ok = false;
- for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
- ok = (*i)->has_track(*track);
-
- float d;
- if(real_speed.size()>1)
- d = get_real_speed(current_speed_step)*(dt/Time::sec);
- else
- d = speed*(dt/Time::sec);
- if(ok)
- {
- SetFlag setf(advancing);
- vehicle.advance(reverse ? -d : d);
- }
- else if(accurate_position)
- {
- overshoot_dist += d;
- if(overshoot_dist>40*layout.get_catalogue().get_scale())
- {
- layout.emergency(name+" has not arrived at sensor");
- accurate_position = false;
- }
- }
- }
- else if(end_of_route && cur_blocks_end==blocks.end())
- {
- set_active(false);
- signal_arrived.emit();
- set_route(0);
- }
-
- if(!blocks.empty() && !blocks.front()->get_sensor_id())
- {
- float dist = get_reserved_distance_until(&*blocks.front(), true);
-
- if(dist>10*layout.get_catalogue().get_scale())
- {
- blocks.front()->reserve(0);
- blocks.pop_front();
- }
- }
-}
-
-void Train::save(list<DataFile::Statement> &st) const
-{
- st.push_back((DataFile::Statement("name"), name));
-
- st.push_back((DataFile::Statement("priority"), priority));
-
- for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
- if(i!=vehicles.begin())
- st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
-
- for(unsigned i=0; i<real_speed.size(); ++i)
- if(real_speed[i].weight)
- st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
-
- if(!blocks.empty() && cur_blocks_end!=blocks.begin())
- {
- BlockList blks(blocks.begin(), BlockList::const_iterator(cur_blocks_end));
- if(reverse)
- reverse_blocks(blks);
-
- BlockIter prev = blks.front().flip();
- st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
-
- for(BlockList::const_iterator i=blks.begin(); i!=blks.end(); ++i)
- st.push_back((DataFile::Statement("block"), (*i)->get_id()));
- }
-
- if(!routes.empty())
- {
- list<RouteRef>::const_iterator i = routes.begin();
- for(; (i!=routes.end() && i->route->is_temporary()); ++i) ;
- if(i!=routes.end())
- st.push_back((DataFile::Statement("route"), i->route->get_name()));
- }
-
- if(timetable)
- {
- DataFile::Statement ss("timetable");
- timetable->save(ss.sub);
- st.push_back(ss);
- }
-}
-
-void Train::control_changed(const Controller::Control &ctrl)
-{
- signal_control_changed.emit(ctrl.name, ctrl.value);
-}
-
-void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
-{
- if(addr==address)
- {
- current_speed_step = speed;
- speed_changing = false;
- pure_speed = false;
- }
-}
-
-void Train::loco_func_event(unsigned addr, unsigned func, bool state)
-{
- if(addr==address || (addr==address+1 && loco_type.get_max_function()>4))
- {
- if(addr==address+1)
- func += 4;
- if(state)
- functions |= 1<<func;
- else
- functions &= ~(1<<func);
-
- signal_function_changed.emit(func, state);
- }
-}
-
-void Train::sensor_event(unsigned addr, bool state)
-{
- if(state)
- {
- // Find the first sensor block from our reserved blocks that isn't this sensor
- BlockList::iterator end;
- unsigned result = 0;
- for(end=cur_blocks_end; end!=blocks.end(); ++end)
- if((*end)->get_sensor_id())
- {
- if((*end)->get_sensor_id()!=addr)
- {
- if(result==0)
- result = 2;
- else if(result==1)
- break;
- }
- else if(result==0)
- result = 1;
- else if(result==2)
- result = 3;
- }
-
- if(result==1)
- {
- // Compute speed and update related state
- float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
-
- if(pure_speed)
- {
- if(current_speed_step>0)
- {
- RealSpeed &rs = real_speed[current_speed_step];
- rs.add(travel_dist/travel_time_secs, travel_time_secs);
- }
- set_status(format("Traveling %d kmh", get_travel_speed()));
- }
-
- travel_dist = 0;
- for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
- {
- travel_dist += (*j)->get_path_length(j->entry());
-
- if((*j)->get_sensor_id()==addr && !advancing)
- {
- TrackIter track = j->track_iter();
- if(reverse)
- {
- track = track.flip();
- vehicles.back()->place(*track, track.entry(), 0, Vehicle::BACK_AXLE);
- }
- else
- vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_AXLE);
- }
- }
- last_entry_time = Time::now();
- pure_speed = true;
- accurate_position = true;
- overshoot_dist = 0;
-
- // Check if we've reached the next route
- if(routes.size()>1)
- {
- const Route &route = *(++routes.begin())->route;
- for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
- if(route.has_track(*j->track_iter()))
- {
- routes.pop_front();
- // XXX Exceptions?
- signal_route_changed.emit(routes.front().route);
- break;
- }
- }
-
- // Move blocks up to the next sensor to our current blocks
- cur_blocks_end = end;
-
- // Try to get more blocks if we're moving
- if(active)
- reserve_more();
- }
- else if(result==3)
- layout.emergency("Sensor for "+name+" triggered out of order");
- }
- else
- {
- const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
-
- // Find the first sensor in our current blocks that's still active
- BlockList::iterator end = blocks.begin();
- for(BlockList::iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
- {
- if((*i)->has_track(*veh.get_track()))
- break;
- if((*i)->get_sensor_id())
- {
- if(layout.get_driver().get_sensor((*i)->get_sensor_id()))
- break;
- else
- {
- end = i;
- ++end;
- }
- }
- }
-
- if(end!=blocks.begin() && end!=cur_blocks_end)
- // Free blocks up to the last inactive sensor
- release_blocks(blocks.begin(), end);
- }
-}
-
-void Train::turnout_path_changed(Track &track)
-{
- for(list<BlockIter>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
- if((*i)->get_turnout_id()==track.get_turnout_id() && !reserving)
- check_turnout_paths(false);
-}
-
-void Train::halt_event(bool h)
-{
- if(h)
- accurate_position = false;
-}
-
-void Train::block_reserved(const Block &block, const Train *train)
-{
- if(&block==pending_block && !train && !reserving)
- reserve_more();
-}
-
-void Train::reserve_more()
-{
- if(!active || blocks.empty() || end_of_route)
- return;
-
- BlockIter start = blocks.back();
-
- pending_block = 0;
-
- // See how many sensor blocks and how much track we already have
- unsigned nsens = 0;
- float dist = 0;
- for(BlockList::const_iterator i=cur_blocks_end; i!=blocks.end(); ++i)
- {
- if((*i)->get_sensor_id())
- ++nsens;
- if(nsens>0)
- dist += (*i)->get_path_length(i->entry());
- }
-
- list<RouteRef>::iterator cur_route = routes.begin();
- advance_route(cur_route, *start.track_iter());
-
- float approach_margin = 50*layout.get_catalogue().get_scale();
- float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
-
- BlockIter block = start;
- list<BlockIter>::iterator good_end = blocks.end();
- Track *divert_track = 0;
- bool try_divert = false;
- Train *blocking_train = 0;
- BlockList contested_blocks;
-
- SetFlag setf(reserving);
-
- while(1)
- {
- BlockIter last = block;
- block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
- if(!block || block->get_endpoints().size()<2)
- {
- if(!blocking_train)
- good_end = blocks.end();
- break;
- }
-
- TrackIter track = block.track_iter();
-
- if(cur_route!=routes.end())
- {
- if(!advance_route(cur_route, *track))
- {
- // Keep the blocks if we arrived at the end of the route
- if(!blocking_train)
- {
- good_end = blocks.end();
- end_of_route = true;
- }
- break;
- }
- }
-
- if(block->get_turnout_id() && !last->get_turnout_id())
- {
- /* We can keep the blocks if we arrive at a turnout from a non-turnout
- block. Having a turnout block as our last reserved block is not good
- as it would limit our diversion possibilities for little benefit. */
- good_end = blocks.end();
- if(nsens>=3 && dist>=min_dist)
- break;
- }
-
- if(blocking_train)
- {
- if(block->get_train()!=blocking_train)
- {
- if(blocking_train->free_block(*contested_blocks.back()))
- {
- // Roll back and start actually reserving the blocks
- block = blocks.back();
- cur_route = routes.begin();
- advance_route(cur_route, *block.track_iter().track());
- if(blocking_train->get_priority()==priority)
- blocking_train->yield_to(*this);
- blocking_train = 0;
- continue;
- }
- else
- {
- yield_to(*blocking_train);
- pending_block = contested_blocks.front().block();
- try_divert = divert_track;
- break;
- }
- }
- else
- {
- contested_blocks.push_back(block);
- continue;
- }
- }
-
- bool reserved = block->reserve(this);
- if(!reserved)
- {
- /* We've found another train. If it wants to exit the block from the
- same endpoint we're trying to enter from or the other way around,
- treat it as coming towards us. Otherwise treat it as going in the
- same direction. */
- Train *other_train = block->get_train();
- int other_entry = other_train->get_entry_to_block(*block);
- if(other_entry<0)
- throw LogicError("Block reservation inconsistency");
-
- unsigned exit = block.reverse().entry();
- unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
- bool entry_conflict = (block.entry()==other_exit);
- bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
- if(!entry_conflict && !last->get_turnout_id())
- /* The other train is not coming to the blocks we're holding, so we
- can keep them. */
- good_end = blocks.end();
-
- int other_prio = other_train->get_priority();
-
- if(!entry_conflict && !exit_conflict && other_prio<priority)
- {
- /* Ask a lesser priority train going to the same direction to free
- the block for us */
- if(other_train->free_block(*block))
- reserved = block->reserve(this);
- }
- else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
- {
- /* A lesser priority train is coming at us, we must ask it to free
- enough blocks to get clear of it to avoid a potential deadlock */
- blocking_train = other_train;
- contested_blocks.clear();
- contested_blocks.push_back(block);
- continue;
- }
- else if(divert_track && (entry_conflict || exit_conflict))
- // We are blocked, but there's a diversion possibility
- try_divert = true;
-
- if(!reserved)
- {
- pending_block = &*block;
- break;
- }
- }
-
- if(block->get_turnout_id())
- {
- const TrackType::Endpoint &track_ep = track.endpoint();
- bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
-
- if(multiple_paths && cur_route!=routes.end() && cur_route->diversion!=block->get_turnout_id())
- /* There's multiple paths to be taken and we are on a route - take
- note of the diversion possibility */
- divert_track = &*track;
- }
-
- if(!contested_blocks.empty() && contested_blocks.front()==block)
- contested_blocks.pop_front();
-
- blocks.push_back(block);
-
- if(cur_blocks_end==blocks.end())
- --cur_blocks_end;
- if(clear_blocks_end==blocks.end())
- --clear_blocks_end;
- if(good_end==blocks.end())
- --good_end;
-
- if(block->get_sensor_id())
- ++nsens;
- if(nsens>0)
- dist += block->get_path_length(block.entry());
- }
-
- // Unreserve blocks that were not good
- release_blocks(good_end, blocks.end());
-
- if(blocks.back()!=start)
- // We got some new blocks, so no longer need to yield
- yielding_to = 0;
-
- check_turnout_paths(true);
-
- // Make any sensorless blocks at the beginning immediately current
- while(cur_blocks_end!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
- ++cur_blocks_end;
-
- if(try_divert && divert(*divert_track))
- reserve_more();
-}
-
-void Train::check_turnout_paths(bool set)
-{
- if(clear_blocks_end==blocks.end())
- return;
-
- for(list<BlockIter>::iterator i=clear_blocks_end; i!=blocks.end(); ++i)
- {
- if((*i)->get_turnout_id())
- {
- TrackIter track = i->track_iter();
- const TrackType::Endpoint &track_ep = track.endpoint();
-
- unsigned path = 0;
- list<BlockIter>::iterator j = i;
- if(++j!=blocks.end())
- {
- TrackIter rev = j->track_iter().flip();
- unsigned mask = rev.endpoint().paths&track_ep.paths;
- for(path=0; mask>1; mask>>=1, ++path) ;
- }
- else
- return;
-
- if(path!=track->get_active_path())
- {
- if(set)
- track->set_active_path(path);
-
- /* Check again, in case the driver was able to service the request
- instantly */
- if(!set || path!=track->get_active_path())
- continue;
- }
- }
-
- if(i==clear_blocks_end)
- ++clear_blocks_end;
- }
-}
-
-float Train::get_reserved_distance_until(const Block *until_block, bool back) const
-{
- if(blocks.empty())
- return 0;
-
- Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
- const VehicleType &vtype = veh.get_type();
-
- TrackIter track(veh.get_track(), veh.get_entry());
- if(!track) // XXX Probably unnecessary
- return 0;
-
- BlockList::const_iterator block = blocks.begin();
- while(block!=clear_blocks_end && !(*block)->has_track(*track))
- ++block;
- if(block==clear_blocks_end || &**block==until_block)
- return 0;
-
- float result = veh.get_offset();
- if(reverse!=back)
- track = track.reverse();
- else
- result = track->get_type().get_path_length(track->get_active_path())-result;
- result -= vtype.get_length()/2;
-
- while(1)
- {
- track = track.next();
- if(!track)
- break;
-
- if(!(*block)->has_track(*track))
- {
- if(back)
- {
- if(block==blocks.begin())
- break;
- --block;
- }
- else
- {
- ++block;
- if(block==clear_blocks_end)
- break;
- }
-
- if(&**block==until_block)
- break;
- }
-
- result += track->get_type().get_path_length(track->get_active_path());
- }
-
- return result;
-}
-
-float Train::get_real_speed(unsigned i) const
-{
- if(i==0)
- return 0;
- if(real_speed[i].weight)
- return real_speed[i].speed;
-
- unsigned low;
- unsigned high;
- for(low=i; low>0; --low)
- if(real_speed[low].weight)
- break;
- for(high=i; high+1<real_speed.size(); ++high)
- if(real_speed[high].weight)
- break;
-
- if(real_speed[high].weight)
- {
- if(real_speed[low].weight)
- {
- float f = float(i-low)/(high-low);
- return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
- }
- else
- return real_speed[high].speed*float(i)/high;
- }
- else if(real_speed[low].weight)
- return real_speed[low].speed*float(i)/low;
- else
- return 0;
-}
-
-unsigned Train::find_speed_step(float real) const
-{
- if(real_speed.size()<=1)
- return 0;
- if(real<=real_speed[1].speed*0.5)
- return 0;
-
- unsigned low = 0;
- unsigned high = 0;
- unsigned last = 0;
- for(unsigned i=0; (!high && i<real_speed.size()); ++i)
- if(real_speed[i].weight)
- {
- last = i;
- if(real_speed[i].speed<real)
- low = i;
- else
- high = i;
- }
- if(!high)
- {
- unsigned limit = real_speed.size()/5;
- if(!low)
- {
- if(real)
- return limit;
- else
- return 0;
- }
- return min(min(static_cast<unsigned>(low*real/real_speed[low].speed), real_speed.size()-1), last+limit);
- }
-
- float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
- return static_cast<unsigned>(low*(1-f)+high*f+0.5);
-}
-
-float Train::get_travel_speed() const
-{
- float speed = get_real_speed(current_speed_step);
- float scale = layout.get_catalogue().get_scale();
- return static_cast<int>(round(speed/scale*3.6/5))*5;
-}
-
-void Train::set_status(const string &s)
-{
- status = s;
- signal_status_changed.emit(s);
-}
-
-void Train::release_blocks()
-{
- release_blocks(blocks.begin(), blocks.end());
-}
-
-void Train::release_blocks(BlockList::iterator begin, BlockList::iterator end)
-{
- while(begin!=end)
- {
- if(begin==cur_blocks_end)
- cur_blocks_end = end;
- if(begin==clear_blocks_end)
- clear_blocks_end = end;
-
- Block &block = **begin;
- blocks.erase(begin++);
- block.reserve(0);
-
- if(begin==blocks.end())
- end_of_route = false;
- }
-}
-
-void Train::reverse_blocks(BlockList &blks) const
-{
- blks.reverse();
- for(BlockList::iterator i=blks.begin(); i!=blks.end(); ++i)
- *i = i->reverse();
-}
-
-bool Train::advance_route(list<RouteRef>::iterator &iter, Track &track)
-{
- while(iter!=routes.end() && !iter->route->has_track(track))
- ++iter;
- if(iter==routes.end())
- return false;
-
- list<RouteRef>::iterator next = iter;
- ++next;
- if(next!=routes.end() && next->diversion && next->route->has_track(track))
- iter = next;
-
- return true;
-}
-
-Route *Train::create_lead_route(Route *lead, const Route *target)
-{
- if(!lead)
- {
- lead = new Route(layout);
- lead->set_name("Lead");
- lead->set_temporary(true);
- }
-
- set<Track *> tracks;
- for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
- {
- const set<Track *> &btracks = (*i)->get_tracks();
- for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
- if(!target || !target->has_track(**j))
- tracks.insert(*j);
- }
-
- lead->add_tracks(tracks);
-
- return lead;
-}
-
-bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from)
-{
- float diversion_len = 0;
- TrackLoopIter track1 = from;
- while(diversion.has_track(*track1))
- {
- unsigned path = diversion.get_path(*track1);
- diversion_len += track1->get_type().get_path_length(path);
-
- track1 = track1.next(path);
-
- if(track1.looped())
- return false;
- }
-
- list<RouteRef>::iterator route = routes.begin();
- if(!advance_route(route, *from))
- return false;
-
- float route_len = 0;
- TrackLoopIter track2 = from;
- while(1)
- {
- unsigned path = route->route->get_path(*track2);
- route_len += track2->get_type().get_path_length(path);
-
- bool ok = (track2!=from && diversion.has_track(*track2));
-
- track2 = track2.next(path);
-
- if(ok)
- break;
-
- if(track2.looped())
- return false;
-
- if(!advance_route(route, *track2))
- return false;
- }
-
- // Must end up at the same place through both routes
- if(track2!=track1)
- return false;
-
- return diversion_len<route_len*1.2;
-}
-
-
-Train::RouteRef::RouteRef(const Route *r, unsigned d):
- route(r),
- diversion(d)
-{ }
-
-
-Train::RealSpeed::RealSpeed():
- speed(0),
- weight(0)
-{ }
-
-void Train::RealSpeed::add(float s, float w)
-{
- speed = (speed*weight+s*w)/(weight+w);
- weight = min(weight+w, 300.0f);
-}
-
-
-Train::Loader::Loader(Train &t):
- DataFile::BasicLoader<Train>(t),
- prev_block(0),
- blocks_valid(true)
-{
- add("block", &Loader::block);
- add("block_hint", &Loader::block_hint);
- add("name", &Loader::name);
- add("priority", &Train::priority);
- add("real_speed", &Loader::real_speed);
- add("route", &Loader::route);
- add("timetable", &Loader::timetable);
- add("vehicle", &Loader::vehicle);
-}
-
-void Train::Loader::finish()
-{
- if(!obj.blocks.empty())
- {
- TrackIter track = obj.blocks.front().track_iter();
- float offset = 2*obj.layout.get_catalogue().get_scale();
- obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
-
- obj.set_status("Stopped");
- }
-}
-
-void Train::Loader::block(unsigned id)
-{
- if(!blocks_valid)
- return;
-
- Block *blk;
- try
- {
- blk = &obj.layout.get_block(id);
- }
- catch(const KeyError &)
- {
- blocks_valid = false;
- return;
- }
-
- int entry = -1;
- if(prev_block)
- entry = blk->get_endpoint_by_link(*prev_block);
- if(entry<0)
- entry = 0;
-
- blk->reserve(&obj);
- obj.blocks.push_back(BlockIter(blk, entry));
-
- if(blk->get_sensor_id())
- obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
-
- prev_block = blk;
-}
-
-void Train::Loader::block_hint(unsigned id)
-{
- try
- {
- prev_block = &obj.layout.get_block(id);
- }
- catch(const KeyError &)
- {
- blocks_valid = false;
- }
-}
-
-void Train::Loader::name(const string &n)
-{
- obj.set_name(n);
-}
-
-void Train::Loader::real_speed(unsigned i, float speed, float weight)
-{
- if(i>=obj.real_speed.size())
- return;
- obj.real_speed[i].speed = speed;
- obj.real_speed[i].weight = weight;
-}
-
-void Train::Loader::route(const string &n)
-{
- obj.set_route(&obj.layout.get_route(n));
-}
-
-void Train::Loader::timetable()
-{
- if(obj.timetable)
- throw InvalidState("A timetable has already been loaded");
-
- obj.timetable = new Timetable(obj);
- load_sub(*obj.timetable);
-}
-
-void Train::Loader::vehicle(ArticleNumber art_nr)
-{
- const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(art_nr);
- Vehicle *veh = new Vehicle(obj.layout, vtype);
- obj.vehicles.back()->attach_back(*veh);
- obj.vehicles.push_back(veh);
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_TRAIN_H_
-#define LIBMARKLIN_TRAIN_H_
-
-#include <sigc++/signal.h>
-#include <sigc++/trackable.h>
-#include <msp/time/timestamp.h>
-#include "block.h"
-#include "blockiter.h"
-#include "controller.h"
-
-namespace Marklin {
-
-class ArticleNumber;
-class Route;
-class Timetable;
-class Vehicle;
-class VehicleType;
-
-class Train: public sigc::trackable
-{
-public:
- class Loader: public Msp::DataFile::BasicLoader<Train>
- {
- private:
- Block *prev_block;
- bool blocks_valid;
-
- public:
- Loader(Train &);
- private:
- virtual void finish();
- void block(unsigned);
- void block_hint(unsigned);
- void name(const std::string &);
- void real_speed(unsigned, float, float);
- void route(const std::string &);
- void timetable();
- void vehicle(ArticleNumber);
- };
-
- sigc::signal<void, const std::string &> signal_name_changed;
- sigc::signal<void, const std::string &, float> signal_control_changed;
- sigc::signal<void, unsigned, bool> signal_function_changed;
- sigc::signal<void, const Route *> signal_route_changed;
- sigc::signal<void> signal_arrived;
- sigc::signal<void, const std::string &> signal_status_changed;
-
-private:
- struct RouteRef
- {
- const Route *route;
- unsigned diversion;
-
- RouteRef(const Route *, unsigned = 0);
- };
-
- struct RealSpeed
- {
- float speed;
- float weight;
-
- RealSpeed();
- void add(float, float);
- };
-
- typedef std::list<BlockIter> BlockList;
-
- Layout &layout;
- const VehicleType &loco_type;
- unsigned address;
- std::string protocol;
- std::string name;
- int priority;
- const Train *yielding_to;
- std::vector<Vehicle *> vehicles;
- BlockList blocks;
- BlockList::iterator cur_blocks_end;
- BlockList::iterator clear_blocks_end;
- Block *pending_block;
- bool reserving;
- bool advancing;
- Controller *controller;
- Timetable *timetable;
- bool active;
- unsigned current_speed_step;
- bool speed_changing;
- bool reverse;
- Msp::Time::TimeStamp stop_timeout;
- unsigned functions;
- std::list<RouteRef> routes;
- bool end_of_route;
- std::string status;
-
- Msp::Time::TimeStamp last_entry_time;
- float travel_dist;
- bool pure_speed;
- std::vector<RealSpeed> real_speed;
- bool accurate_position;
- float overshoot_dist;
-
-public:
- Train(Layout &, const VehicleType &, unsigned, const std::string &);
- ~Train();
-
- Layout &get_layout() const { return layout; }
- const VehicleType &get_locomotive_type() const { return loco_type; }
- unsigned get_address() const { return address; }
- const std::string &get_protocol() const { return protocol; }
- void set_name(const std::string &);
- const std::string &get_name() const { return name; }
- void set_priority(int);
- void yield_to(const Train &);
- int get_priority() const { return priority; }
- Controller &get_controller() const { return *controller; }
-
- void add_vehicle(const VehicleType &);
- void remove_vehicle(unsigned);
- unsigned get_n_vehicles() const;
- Vehicle &get_vehicle(unsigned);
- const Vehicle &get_vehicle(unsigned) const;
-
- void set_control(const std::string &, float);
- void set_active(bool);
- void set_function(unsigned, bool);
- float get_control(const std::string &) const;
- float get_speed() const;
- bool is_active() const { return active; }
- bool get_function(unsigned) const;
- unsigned get_functions() const { return functions; }
-
- void set_timetable(Timetable *);
- Timetable *get_timetable() { return timetable; }
-
- bool set_route(const Route *);
- bool go_to(Track &);
- bool divert(Track &);
- const Route *get_route() const;
- void place(Block &, unsigned);
- void unplace();
- bool is_placed() const { return !blocks.empty(); }
- bool free_block(Block &);
- void free_noncritical_blocks();
- int get_entry_to_block(Block &) const;
- float get_reserved_distance() const;
-
- const std::string &get_status() const { return status; }
-
- void tick(const Msp::Time::TimeStamp &, const Msp::Time::TimeDelta &);
-
- void save(std::list<Msp::DataFile::Statement> &) const;
-private:
- void control_changed(const Controller::Control &);
- void loco_speed_event(unsigned, unsigned, bool);
- void loco_func_event(unsigned, unsigned, bool);
- void sensor_event(unsigned, bool);
- void turnout_path_changed(Track &);
- void halt_event(bool);
- void block_reserved(const Block &, const Train *);
- void reserve_more();
- void check_turnout_paths(bool);
- float get_reserved_distance_until(const Block *, bool) const;
- float get_real_speed(unsigned) const;
- unsigned find_speed_step(float) const;
- float get_travel_speed() const;
- void set_status(const std::string &);
- void release_blocks();
- void release_blocks(BlockList::iterator, BlockList::iterator);
- void reverse_blocks(BlockList &) const;
- bool advance_route(std::list<RouteRef>::iterator &, Track &);
- Route *create_lead_route(Route *, const Route *);
- bool is_valid_diversion(const Route &, const TrackIter &);
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <cmath>
-#include "catalogue.h"
-#include "driver.h"
-#include "layout.h"
-#include "track.h"
-#include "trackiter.h"
-#include "tracktype.h"
-#include "vehicle.h"
-#include "vehicletype.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Vehicle::Vehicle(Layout &l, const VehicleType &t):
- layout(l),
- type(t),
- next(0),
- prev(0),
- direction(0),
- bogie_dirs(type.get_bogies().size()),
- front_sensor(0),
- back_sensor(0)
-{
- layout.add_vehicle(*this);
-}
-
-Vehicle::~Vehicle()
-{
- if(next)
- detach_back();
- if(prev)
- detach_front();
- layout.remove_vehicle(*this);
-}
-
-void Vehicle::attach_back(Vehicle &veh)
-{
- if(next || veh.prev)
- throw InvalidState("Already attached");
-
- next = &veh;
- veh.prev = this;
-
- if(track_pos.track)
- propagate_backward();
-}
-
-void Vehicle::attach_front(Vehicle &veh)
-{
- if(prev || veh.next)
- throw InvalidState("Already attached");
-
- prev = &veh;
- veh.next = this;
-
- if(prev->get_track())
- prev->propagate_backward();
-}
-
-void Vehicle::detach_back()
-{
- if(!next)
- throw InvalidState("Not attached");
-
- next->prev = 0;
- next = 0;
-}
-
-void Vehicle::detach_front()
-{
- if(!prev)
- throw InvalidState("Not attached");
-
- prev->next = 0;
- prev = 0;
-}
-
-void Vehicle::place(Track &t, unsigned e, float o, PlaceMode m)
-{
- track_pos = TrackPosition(&t, e, o);
-
- if(m==FRONT_AXLE)
- track_pos.advance(-type.get_front_axle_offset());
- else if(m==FRONT_BUFFER)
- track_pos.advance(-type.get_length()/2);
- else if(m==BACK_AXLE)
- track_pos.advance(-type.get_back_axle_offset());
- else if(m==BACK_BUFFER)
- track_pos.advance(type.get_length()/2);
-
- update_position();
- propagate_position();
-}
-
-void Vehicle::unplace()
-{
- if(!track_pos.track)
- return;
-
- track_pos = TrackPosition();
-
- if(prev)
- prev->unplace();
- if(next)
- next->unplace();
-}
-
-void Vehicle::advance(float d)
-{
- track_pos.advance(d);
- update_position();
- propagate_position();
-}
-
-float Vehicle::get_bogie_direction(unsigned i) const
-{
- if(i>=bogie_dirs.size())
- throw InvalidParameterValue("Bogie index out of range");
- return bogie_dirs[i];
-}
-
-void Vehicle::update_position()
-{
- TrackPoint tp;
-
- const vector<VehicleType::Axle> &axles = type.get_axles();
- const vector<VehicleType::Bogie> &bogies = type.get_bogies();
- if(axles.size()>=2)
- {
- float wheelbase = axles.front().position-axles.back().position;
- tp = get_point(track_pos, wheelbase, -axles.back().position/wheelbase);
- }
- else if(bogies.size()>=2)
- {
- TrackPosition front = track_pos;
- front.advance(bogies.front().position);
- TrackPosition back = track_pos;
- back.advance(bogies.back().position);
- float bogie_spacing = bogies.front().position-bogies.back().position;
- adjust_for_distance(front, back, bogie_spacing);
-
- const vector<VehicleType::Axle> &front_axles = bogies.front().axles;
- float wheelbase = front_axles.front().position-front_axles.back().position;
- TrackPoint front_point = get_point(front, wheelbase, -front_axles.back().position/wheelbase);
-
- const vector<VehicleType::Axle> &back_axles = bogies.back().axles;
- wheelbase = back_axles.front().position-back_axles.back().position;
- TrackPoint back_point = get_point(back, wheelbase, -back_axles.back().position/wheelbase);
-
- tp = get_point(front_point.pos, back_point.pos, -bogies.back().position/bogie_spacing);
-
- bogie_dirs.front() = front_point.dir-tp.dir;
- bogie_dirs.back() = back_point.dir-tp.dir;
- }
- else
- tp = track_pos.get_point();
-
- if(!prev)
- check_sensor(type.get_front_axle_offset(), front_sensor);
- if(!next)
- check_sensor(type.get_back_axle_offset(), back_sensor);
-
- position = tp.pos;
- position.z += layout.get_catalogue().get_rail_elevation();
- direction = tp.dir;
-}
-
-void Vehicle::update_position_from(const Vehicle &veh)
-{
- int sign = (&veh==prev ? -1 : 1);
-
- float tdist = (type.get_length()+veh.type.get_length())/2;
- float margin = layout.get_catalogue().get_scale();
-
- float dist = distance(veh.position, position);
- if(dist<tdist-margin || dist>tdist+margin)
- {
- track_pos = veh.track_pos;
- track_pos.advance(sign*tdist);
- update_position();
-
- dist = distance(veh.position, position);
- }
-
- track_pos.advance(sign*(tdist-dist));
- update_position();
-}
-
-void Vehicle::propagate_position()
-{
- if(prev)
- propagate_forward();
- if(next)
- propagate_backward();
-}
-
-void Vehicle::propagate_forward()
-{
- prev->update_position_from(*this);
-
- if(prev->prev)
- prev->propagate_forward();
-}
-
-void Vehicle::propagate_backward()
-{
- next->update_position_from(*this);
-
- if(next->next)
- next->propagate_backward();
-}
-
-void Vehicle::check_sensor(float offset, unsigned &sensor)
-{
- TrackPosition pos = track_pos;
- pos.advance(offset);
- unsigned s = pos.track->get_sensor_id();
- if(s!=sensor)
- {
- /* Sensor ID under axle has changed. Deduce movement direction by using
- the sensor ID under the midpoint of the vehicle. */
- /* XXX This depends on the simulation running fast enough. Something
- more robust would be preferable. */
- unsigned old = sensor;
- sensor = s;
- unsigned mid = track_pos.track->get_sensor_id();
-
- if(s && s!=mid)
- /* There's a sensor and it's different from mid. We've just entered
- that sensor. */
- layout.get_driver().set_sensor(sensor, true);
- if(old && old!=mid)
- /* A sensor was under the axle and it was different from mid. We've
- just left that sensor. */
- layout.get_driver().set_sensor(old, false);
- }
-}
-
-void Vehicle::adjust_for_distance(TrackPosition &front, TrackPosition &back, float tdist, float ratio) const
-{
- float margin = 0.01*layout.get_catalogue().get_scale();
- int adjust_dir = 0;
- while(1)
- {
- Point front_point = front.get_point().pos;
- Point back_point = back.get_point().pos;
-
- float dx = front_point.x-back_point.x;
- float dy = front_point.y-back_point.y;
- float dz = front_point.z-back_point.z;
- float dist = sqrt(dx*dx+dy*dy+dz*dz);
-
- float diff = tdist-dist;
- if(diff<-margin && adjust_dir<=0)
- {
- diff -= margin;
- adjust_dir = -1;
- }
- else if(diff>margin && adjust_dir>=0)
- {
- diff += margin;
- adjust_dir = 1;
- }
- else
- return;
-
- front.advance(diff*(1-ratio));
- back.advance(-diff*ratio);
- }
-}
-
-TrackPoint Vehicle::get_point(const Point &front, const Point &back, float ratio) const
-{
- float dx = front.x-back.x;
- float dy = front.y-back.y;
- float dz = front.z-back.z;
-
- TrackPoint tp;
- tp.pos = Point(back.x+dx*ratio, back.y+dy*ratio, back.z+dz*ratio);
- tp.dir = atan2(dy, dx);
-
- return tp;
-}
-
-TrackPoint Vehicle::get_point(const TrackPosition &pos, float tdist, float ratio) const
-{
- TrackPosition front = pos;
- front.advance(tdist*(1-ratio));
-
- TrackPosition back = pos;
- back.advance(-tdist*ratio);
-
- adjust_for_distance(front, back, tdist, ratio);
- return get_point(front.get_point().pos, back.get_point().pos, ratio);
-}
-
-
-Vehicle::TrackPosition::TrackPosition():
- track(0),
- ep(0),
- offs(0)
-{ }
-
-Vehicle::TrackPosition::TrackPosition(Track *t, unsigned e, float o):
- track(t),
- ep(e),
- offs(o)
-{ }
-
-void Vehicle::TrackPosition::advance(float d)
-{
- if(!track)
- return;
-
- offs += d;
- TrackIter iter(track, ep);
- while(iter)
- {
- float path_len = iter->get_type().get_path_length(iter->get_active_path());
-
- if(offs>path_len)
- {
- offs -= path_len;
- iter = iter.next();
- }
- else
- break;
- }
-
- while(iter && offs<0)
- {
- iter = iter.flip().reverse();
-
- if(iter)
- {
- float path_len = iter->get_type().get_path_length(iter->get_active_path());
- offs += path_len;
- }
- }
-
- track = iter.track();
- ep = iter.entry();
- if(!track)
- offs = 0;
-}
-
-TrackPoint Vehicle::TrackPosition::get_point() const
-{
- if(track)
- return track->get_point(ep, offs);
- else
- return TrackPoint();
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_VEHICLE_H_
-#define LIBMARKLIN_VEHICLE_H_
-
-#include "geometry.h"
-
-namespace Marklin {
-
-class Layout;
-class Track;
-class VehicleType;
-
-class Vehicle
-{
-public:
- enum PlaceMode
- {
- CENTER,
- FRONT_AXLE,
- FRONT_BUFFER,
- BACK_AXLE,
- BACK_BUFFER
- };
-
-private:
- struct TrackPosition
- {
- Track *track;
- unsigned ep;
- float offs;
-
- TrackPosition();
- TrackPosition(Track *, unsigned, float);
- void advance(float);
- TrackPoint get_point() const;
- };
-
- Layout &layout;
- const VehicleType &type;
- Vehicle *next;
- Vehicle *prev;
- TrackPosition track_pos;
- Point position;
- float direction;
- std::vector<float> bogie_dirs;
- unsigned front_sensor;
- unsigned back_sensor;
-
-public:
- Vehicle(Layout &, const VehicleType &);
- ~Vehicle();
-
- const VehicleType &get_type() const { return type; }
-
- void attach_back(Vehicle &);
- void attach_front(Vehicle &);
- void detach_back();
- void detach_front();
- Vehicle *get_next() const { return next; }
- Vehicle *get_previous() const { return prev; }
-
- void place(Track &, unsigned, float, PlaceMode = CENTER);
- void unplace();
- void advance(float);
- Track *get_track() const { return track_pos.track; }
- unsigned get_entry() const { return track_pos.ep; }
- float get_offset() const { return track_pos.offs; }
- const Point &get_position() const { return position; }
- float get_direction() const { return direction; }
- float get_bogie_direction(unsigned) const;
-private:
- void update_position();
- void update_position_from(const Vehicle &);
- void propagate_position();
- void propagate_forward();
- void propagate_backward();
- void check_sensor(float, unsigned &);
-
- void adjust_for_distance(TrackPosition &, TrackPosition &, float, float = 0.5) const;
- TrackPoint get_point(const Point &, const Point &, float = 0.5) const;
- TrackPoint get_point(const TrackPosition &, float, float = 0.5) const;
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include "vehicletype.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-VehicleType::VehicleType(const ArticleNumber &an):
- art_nr(an),
- locomotive(false),
- length(0),
- width(0),
- height(0)
-{ }
-
-unsigned VehicleType::get_max_function() const
-{
- if(functions.empty())
- return 0;
- return (--functions.end())->first;
-}
-
-float VehicleType::get_front_axle_offset() const
-{
- float front = length/2;
- if(!axles.empty())
- front = axles.front().position;
- if(!bogies.empty())
- {
- const Bogie &bogie = bogies.front();
- front = max(front, bogie.position+bogie.axles.front().position);
- }
- return front;
-}
-
-float VehicleType::get_back_axle_offset() const
-{
- float back = -length/2;
- if(!axles.empty())
- back = axles.back().position;
- if(!bogies.empty())
- {
- const Bogie &bogie = bogies.back();
- back = min(back, bogie.position+bogie.axles.back().position);
- }
- return back;
-}
-
-
-VehicleType::Axle::Axle():
- position(0),
- wheel_dia(0),
- powered(false)
-{ }
-
-
-VehicleType::Bogie::Bogie():
- position(0),
- rotate_object(false)
-{ }
-
-
-VehicleType::Loader::Loader(VehicleType &vt):
- DataFile::ObjectLoader<VehicleType>(vt)
-{
- add("axle", &Loader::axle);
- add("bogie", &Loader::bogie);
- add("function", &Loader::function);
- add("height", &Loader::height);
- add("length", &Loader::length);
- add("locomotive", &VehicleType::locomotive);
- add("object", &VehicleType::object);
- add("name", &VehicleType::name);
- add("width", &Loader::width);
-}
-
-void VehicleType::Loader::axle()
-{
- Axle axl;
- load_sub(axl);
- obj.axles.push_back(axl);
-}
-
-void VehicleType::Loader::bogie()
-{
- Bogie bog;
- load_sub(bog);
- obj.bogies.push_back(bog);
-}
-
-void VehicleType::Loader::function(unsigned i, const string &f)
-{
- obj.functions[i] = f;
-}
-
-void VehicleType::Loader::height(float h)
-{
- obj.height = h/1000;
-}
-
-void VehicleType::Loader::length(float l)
-{
- obj.length = l/1000;
-}
-
-void VehicleType::Loader::width(float w)
-{
- obj.width = w/1000;
-}
-
-
-VehicleType::Axle::Loader::Loader(Axle &a):
- DataFile::ObjectLoader<Axle>(a)
-{
- add("object", &Axle::object);
- add("position", &Loader::position);
- add("powered", &Axle::powered);
- add("wheel_diameter", &Loader::wheel_diameter);
-}
-
-void VehicleType::Axle::Loader::position(float p)
-{
- obj.position = p/1000;
-}
-
-void VehicleType::Axle::Loader::wheel_diameter(float d)
-{
- obj.wheel_dia = d/1000;
-}
-
-
-VehicleType::Bogie::Loader::Loader(Bogie &b):
- DataFile::ObjectLoader<Bogie>(b)
-{
- add("axle", &Loader::axle);
- add("object", &Bogie::object);
- add("position", &Loader::position);
- add("rotate_object", &Bogie::rotate_object);
-}
-
-void VehicleType::Bogie::Loader::axle()
-{
- Axle axl;
- load_sub(axl);
- obj.axles.push_back(axl);
-}
-
-void VehicleType::Bogie::Loader::position(float p)
-{
- obj.position = p/1000;
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_VEHICLETYPE_H_
-#define LIBMARKLIN_VEHICLETYPE_H_
-
-#include <msp/datafile/objectloader.h>
-#include "articlenumber.h"
-
-namespace Marklin {
-
-class VehicleType
-{
-public:
- class Loader: public Msp::DataFile::ObjectLoader<VehicleType>
- {
- public:
- Loader(VehicleType &);
- private:
- void axle();
- void bogie();
- void function(unsigned, const std::string &);
- void height(float);
- void length(float);
- void width(float);
- };
-
- struct Axle
- {
- class Loader: public Msp::DataFile::ObjectLoader<Axle>
- {
- public:
- Loader(Axle &);
- private:
- void position(float);
- void wheel_diameter(float);
- };
-
- float position;
- float wheel_dia;
- bool powered;
- std::string object;
-
- Axle();
- };
-
- struct Bogie
- {
- class Loader: public Msp::DataFile::ObjectLoader<Bogie>
- {
- public:
- Loader(Bogie &);
- private:
- void axle();
- void position(float);
- };
-
- float position;
- std::vector<Axle> axles;
- std::string object;
- bool rotate_object;
-
- Bogie();
- };
-
-private:
- ArticleNumber art_nr;
- std::string name;
- bool locomotive;
- std::map<unsigned, std::string> functions;
- float length;
- float width;
- float height;
- std::vector<Axle> axles;
- std::vector<Bogie> bogies;
- std::string object;
-
-public:
- VehicleType(const ArticleNumber &);
-
- const ArticleNumber &get_article_number() const { return art_nr; }
- const std::string &get_name() const { return name; }
- bool is_locomotive() const { return locomotive; }
- unsigned get_max_function() const;
- const std::map<unsigned, std::string> &get_functions() const { return functions; }
- float get_length() const { return length; }
- float get_width() const { return width; }
- float get_height() const { return height; }
- const std::vector<Axle> &get_axles() const { return axles; }
- const std::vector<Bogie> &get_bogies() const { return bogies; }
- float get_front_axle_offset() const;
- float get_back_axle_offset() const;
- const std::string &get_object() const { return object; }
-};
-
-} // namespace Marklin
-
-#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include "aicontrol.h"
+#include "catalogue.h"
+#include "layout.h"
+#include "train.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+AIControl::AIControl(Train &t, Controller *n):
+ train(t),
+ next_ctrl(n),
+ target_speed(Control::continuous("speed", 0, 1000)),
+ blocked(false),
+ approach(false)
+{
+ target_speed.set(0);
+
+ train.signal_arrived.connect(sigc::mem_fun(this, &AIControl::arrived));
+ next_ctrl->signal_control_changed.connect(sigc::mem_fun(this, &AIControl::control_changed));
+}
+
+AIControl::~AIControl()
+{
+ delete next_ctrl;
+}
+
+void AIControl::set_control(const string &n, float v)
+{
+ if(n=="speed")
+ {
+ if(v && !train.is_active())
+ train.set_active(true);
+
+ target_speed.set(v);
+ if(!blocked)
+ {
+ float approach_speed = 5*train.get_layout().get_catalogue().get_scale();
+ if(approach && target_speed.value>approach_speed)
+ next_ctrl->set_control("speed", approach_speed);
+ else
+ next_ctrl->set_control("speed", target_speed.value);
+ }
+
+ signal_control_changed.emit(target_speed);
+ }
+ else
+ next_ctrl->set_control(n, v);
+}
+
+const Controller::Control &AIControl::get_control(const string &n) const
+{
+ if(n=="speed")
+ return target_speed;
+ else
+ return next_ctrl->get_control(n);
+}
+
+float AIControl::get_speed() const
+{
+ return next_ctrl->get_speed();
+}
+
+bool AIControl::get_reverse() const
+{
+ return next_ctrl->get_reverse();
+}
+
+float AIControl::get_braking_distance() const
+{
+ return next_ctrl->get_braking_distance();
+}
+
+void AIControl::tick(const Time::TimeDelta &dt)
+{
+ float scale = train.get_layout().get_catalogue().get_scale();
+ float rsv_dist = train.get_reserved_distance();
+ float brake_dist = next_ctrl->get_braking_distance();
+ float approach_margin = 50*scale;
+ float approach_speed = 5*scale;
+ float margin = 10*scale;
+
+ if(!blocked && rsv_dist<brake_dist+margin)
+ {
+ blocked = true;
+ next_ctrl->set_control("speed", 0);
+ }
+ else if((!approach && rsv_dist<brake_dist*1.3+approach_margin) || (blocked && rsv_dist>brake_dist+margin*2))
+ {
+ blocked = false;
+ approach = true;
+ if(target_speed.value>approach_speed)
+ next_ctrl->set_control("speed", approach_speed);
+ else
+ next_ctrl->set_control("speed", target_speed.value);
+ }
+ else if((blocked || approach) && rsv_dist>brake_dist*1.3+approach_margin*2)
+ {
+ blocked = false;
+ approach = false;
+ next_ctrl->set_control("speed", target_speed.value);
+ }
+
+ next_ctrl->tick(dt);
+
+ if(!target_speed.value && !next_ctrl->get_speed() && train.is_active())
+ train.set_active(false);
+}
+
+void AIControl::control_changed(const Control &ctrl)
+{
+ if(ctrl.name!="speed")
+ signal_control_changed.emit(ctrl);
+}
+
+void AIControl::arrived()
+{
+ set_control("speed", 0);
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_AICONTROL_H_
+#define LIBR2C2_AICONTROL_H_
+
+#include <sigc++/trackable.h>
+#include "controller.h"
+
+namespace R2C2 {
+
+class Train;
+
+class AIControl: public Controller, public sigc::trackable
+{
+private:
+ Train &train;
+ Controller *next_ctrl;
+ Control target_speed;
+ bool blocked;
+ bool approach;
+
+public:
+ AIControl(Train &, Controller *);
+ virtual ~AIControl();
+
+ virtual void set_control(const std::string &, float);
+ virtual const Control &get_control(const std::string &) const;
+
+ virtual float get_speed() const;
+ virtual bool get_reverse() const;
+ virtual float get_braking_distance() const;
+
+ virtual void tick(const Msp::Time::TimeDelta &);
+
+private:
+ void control_changed(const Control &);
+ void arrived();
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/strings/utils.h>
+#include "articlenumber.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+ArticleNumber::ArticleNumber(unsigned n)
+{
+ Part part;
+ part.number = n;
+ part.letter = 0;
+ parts.push_back(part);
+}
+
+ArticleNumber::ArticleNumber(const string &s)
+{
+ vector<string> sparts = split(s, '-');
+ for(vector<string>::iterator i=sparts.begin(); i!=sparts.end(); ++i)
+ {
+ if(i->empty())
+ throw InvalidParameterValue("Malformed article number");
+
+ unsigned nondigit = i->size();
+ for(unsigned j=0; j<i->size(); ++j)
+ if(!isdigit((*i)[j]))
+ {
+ nondigit = j;
+ break;
+ }
+
+ if(!nondigit || nondigit<i->size()-1)
+ throw InvalidParameterValue("Malformed article number");
+
+ Part part;
+ part.number = lexical_cast<unsigned>(i->substr(0, nondigit));
+ part.letter = nondigit<i->size() ? (*i)[nondigit] : 0;
+ parts.push_back(part);
+ }
+}
+
+string ArticleNumber::str() const
+{
+ string result;
+ for(vector<Part>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
+ {
+ if(!result.empty())
+ result += '-';
+
+ result += lexical_cast(i->number);
+ if(i->letter)
+ result += i->letter;
+ }
+
+ return result;
+}
+
+bool ArticleNumber::operator<(const ArticleNumber &other) const
+{
+ return parts<other.parts;
+}
+
+
+bool ArticleNumber::Part::operator<(const Part &other) const
+{
+ if(number!=other.number)
+ return number<other.number;
+ return letter<other.letter;
+}
+
+
+void operator>>(const LexicalConverter &conv, ArticleNumber &art_nr)
+{
+ art_nr = ArticleNumber(conv.get());
+}
+
+void operator<<(LexicalConverter &conv, const ArticleNumber &art_nr)
+{
+ conv.result(art_nr.str());
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_ARTICLENUMBER_H_
+#define LIBR2C2_ARTICLENUMBER_H_
+
+#include <string>
+#include <vector>
+#include <msp/strings/lexicalcast.h>
+
+namespace R2C2 {
+
+class ArticleNumber
+{
+private:
+ struct Part
+ {
+ unsigned number;
+ char letter;
+
+ bool operator<(const Part &) const;
+ };
+
+ std::vector<Part> parts;
+
+public:
+ ArticleNumber() { }
+ ArticleNumber(unsigned);
+ ArticleNumber(const std::string &);
+
+ std::string str() const;
+
+ bool operator<(const ArticleNumber &) const;
+};
+
+void operator>>(const Msp::LexicalConverter &, ArticleNumber &);
+void operator<<(Msp::LexicalConverter &, const ArticleNumber &);
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <algorithm>
+#include "block.h"
+#include "layout.h"
+#include "route.h"
+#include "trackiter.h"
+#include "tracktype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+Block::Block(Layout &l, Track &start):
+ layout(l),
+ id(0),
+ sensor_id(start.get_sensor_id()),
+ turnout_id(start.get_turnout_id()),
+ train(0)
+{
+ tracks.insert(&start);
+ start.set_block(this);
+
+ list<Track *> queue;
+ queue.push_back(&start);
+
+ while(!queue.empty())
+ {
+ Track *track = queue.front();
+ queue.erase(queue.begin());
+
+ const vector<Track *> &links = track->get_links();
+ for(unsigned i=0; i<links.size(); ++i)
+ if(links[i] && !tracks.count(links[i]))
+ {
+ if(links[i]->get_sensor_id()==sensor_id && links[i]->get_turnout_id()==turnout_id)
+ {
+ queue.push_back(links[i]);
+ tracks.insert(links[i]);
+ links[i]->set_block(this);
+ }
+ else
+ endpoints.push_back(Endpoint(track, i));
+ }
+ }
+
+ determine_id();
+
+ for(unsigned i=0; i<endpoints.size(); ++i)
+ {
+ unsigned path = 1<<i;
+ endpoints[i].paths |= path;
+ find_paths(TrackIter(endpoints[i].track, endpoints[i].track_ep), path);
+ }
+
+ layout.add_block(*this);
+}
+
+Block::~Block()
+{
+ set<Track *> trks = tracks;
+ tracks.clear();
+ for(set<Track *>::iterator i=trks.begin(); i!=trks.end(); ++i)
+ (*i)->set_block(0);
+
+ for(vector<Endpoint>::iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
+ if(Block *blk = i->link)
+ {
+ i->link = 0;
+ blk->break_link(*this);
+ }
+
+ layout.remove_block(*this);
+}
+
+bool Block::has_track(Track &t) const
+{
+ return tracks.count(&t);
+}
+
+const Block::Endpoint &Block::get_endpoint(unsigned i) const
+{
+ if(i>=endpoints.size())
+ throw InvalidParameterValue("Endpoint index out of range");
+
+ return endpoints[i];
+}
+
+int Block::get_endpoint_by_link(Block &other) const
+{
+ for(unsigned i=0; i<endpoints.size(); ++i)
+ if(endpoints[i].link==&other)
+ return i;
+
+ return -1;
+}
+
+float Block::get_path_length(unsigned entry, const Route *route) const
+{
+ if(entry>=endpoints.size())
+ throw InvalidParameterValue("Endpoint index out of range");
+
+ TrackIter t_iter(endpoints[entry].track, endpoints[entry].track_ep);
+
+ float result = 0;
+ while(t_iter && has_track(*t_iter))
+ {
+ unsigned path = (route ? route->get_path(*t_iter) : t_iter->get_active_path());
+ result += t_iter->get_type().get_path_length(path);
+
+ t_iter = t_iter.next(path);
+ }
+
+ return result;
+}
+
+void Block::check_link(Block &other)
+{
+ for(vector<Endpoint>::iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
+ {
+ if(i->link)
+ continue;
+
+ for(vector<Endpoint>::iterator j=other.endpoints.begin(); j!=other.endpoints.end(); ++j)
+ if(j->track==i->track->get_link(i->track_ep) && j->track->get_link(j->track_ep)==i->track && !j->link)
+ {
+ i->link = &other;
+ j->link = this;
+
+ determine_id();
+ other.determine_id();
+ }
+ }
+}
+
+void Block::break_link(Block &other)
+{
+ for(vector<Endpoint>::iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
+ if(i->link==&other)
+ {
+ i->link = 0;
+ other.break_link(*this);
+ determine_id();
+ }
+}
+
+Block *Block::get_link(unsigned epi) const
+{
+ if(epi>=endpoints.size())
+ throw InvalidParameterValue("Endpoint index out of range");
+ return endpoints[epi].link;
+}
+
+bool Block::reserve(Train *t)
+{
+ if(!t || !train)
+ {
+ train = t;
+ layout.signal_block_reserved.emit(*this, train);
+ return true;
+ }
+ else
+ return false;
+}
+
+void Block::find_paths(TrackIter track, unsigned path)
+{
+ unsigned mask = track.endpoint().paths;
+ for(unsigned i=0; mask>>i; ++i)
+ if(mask&(1<<i))
+ {
+ TrackIter next = track.next(i);
+ if(!next)
+ continue;
+ else if(has_track(*next))
+ find_paths(track.next(i), path);
+ else
+ {
+ next = next.flip();
+ for(vector<Endpoint>::iterator j=endpoints.begin(); j!=endpoints.end(); ++j)
+ if(j->track==next.track() && j->track_ep==next.entry())
+ j->paths |= path;
+ }
+ }
+}
+
+void Block::determine_id()
+{
+ if(sensor_id)
+ id = 0x1000|sensor_id;
+ else if(turnout_id)
+ id = 0x2000|turnout_id;
+ else if(endpoints.size()==2)
+ {
+ unsigned id1 = endpoints[0].link ? endpoints[0].link->get_id() : 1;
+ unsigned id2 = endpoints[1].link ? endpoints[1].link->get_id() : 1;
+ if(id2<id1)
+ swap(id1, id2);
+ id = (id1<<16)|id2;
+ }
+ else if(endpoints.size()==1)
+ {
+ unsigned id1 = endpoints[0].link ? endpoints[0].link->get_id() : 1;
+ id = 0x10000 | id1;
+ }
+}
+
+
+Block::Endpoint::Endpoint(Track *t, unsigned e):
+ track(t),
+ track_ep(e),
+ link(0),
+ paths(0)
+{ }
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_BLOCK_H_
+#define LIBR2C2_BLOCK_H_
+
+#include <list>
+#include <set>
+#include "track.h"
+
+namespace R2C2 {
+
+class Layout;
+class Route;
+class TrackIter;
+class Train;
+
+class Block
+{
+public:
+ struct Endpoint
+ {
+ Track *track;
+ unsigned track_ep;
+ Block *link;
+ unsigned paths;
+
+ Endpoint(Track *, unsigned);
+ };
+
+private:
+ Layout &layout;
+ unsigned id;
+ unsigned sensor_id;
+ unsigned turnout_id;
+ std::set<Track *> tracks;
+ std::vector<Endpoint> endpoints;
+ Train *train;
+
+public:
+ Block(Layout &, Track &);
+ ~Block();
+
+ unsigned get_id() const { return id; }
+ unsigned get_sensor_id() const { return sensor_id; }
+ unsigned get_turnout_id() const { return turnout_id; }
+ const std::set<Track *> &get_tracks() const { return tracks; }
+ bool has_track(Track &) const;
+ const std::vector<Endpoint> &get_endpoints() const { return endpoints; }
+ const Endpoint &get_endpoint(unsigned) const;
+ int get_endpoint_by_link(Block &) const;
+ float get_path_length(unsigned, const Route * = 0) const;
+ void check_link(Block &);
+ void break_link(Block &);
+ Block *get_link(unsigned) const;
+ bool reserve(Train *);
+ Train *get_train() const { return train; }
+ void print_debug();
+private:
+ void find_paths(TrackIter, unsigned);
+ void determine_id();
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/core/except.h>
+#include "block.h"
+#include "blockiter.h"
+#include "route.h"
+#include "trackiter.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+BlockIter::BlockIter():
+ _block(0),
+ _entry(0)
+{ }
+
+BlockIter::BlockIter(Block *b, unsigned e):
+ _block(b),
+ _entry(b ? e : 0)
+{
+ if(_block && _entry>_block->get_endpoints().size())
+ throw InvalidParameterValue("Endpoint index not valid for block");
+}
+
+TrackIter BlockIter::track_iter() const
+{
+ if(!_block)
+ return TrackIter();
+
+ const Block::Endpoint &ep = _block->get_endpoint(_entry);
+ return TrackIter(ep.track, ep.track_ep);
+}
+
+const Block::Endpoint &BlockIter::endpoint() const
+{
+ if(!_block)
+ throw InvalidState("BlockIter is null");
+
+ return _block->get_endpoint(_entry);
+}
+
+int BlockIter::get_exit(const Route *route) const
+{
+ const vector<Block::Endpoint> &eps = _block->get_endpoints();
+ TrackIter t_iter = track_iter();
+
+ while(t_iter)
+ {
+ if(!_block->has_track(*t_iter))
+ throw LogicError("Block traversal strayed out of the block");
+
+ unsigned path = (route ? route->get_path(*t_iter) : t_iter->get_active_path());
+ TrackIter t_exit = t_iter.reverse(path);
+
+ for(unsigned i=0; i<eps.size(); ++i)
+ if(eps[i].track==t_exit.track() && eps[i].track_ep==t_exit.entry())
+ return i;
+
+ t_iter = t_exit.flip();
+ }
+
+ return -1;
+}
+
+BlockIter BlockIter::next(const Route *route) const
+{
+ if(!_block)
+ return BlockIter();
+
+ int exit = get_exit(route);
+ if(exit<0)
+ return BlockIter();
+
+ BlockIter result;
+ result._block = _block->get_link(exit);
+ result._entry = (result._block ? result._block->get_endpoint_by_link(*_block) : 0);
+
+ return result;
+}
+
+BlockIter BlockIter::reverse(const Route *route) const
+{
+ if(!_block)
+ return BlockIter();
+
+ int exit = get_exit(route);
+ if(exit<0)
+ return BlockIter();
+
+ return BlockIter(_block, exit);
+}
+
+BlockIter BlockIter::flip() const
+{
+ if(!_block)
+ return BlockIter();
+
+ BlockIter result;
+ result._block = _block->get_link(_entry);
+ result._entry = (result._block ? result._block->get_endpoint_by_link(*_block) : 0);
+
+ return result;
+}
+
+Block &BlockIter::operator*() const
+{
+ if(!_block)
+ throw InvalidState("BlockIter is null");
+
+ return *_block;
+}
+
+bool BlockIter::operator==(const BlockIter &other) const
+{
+ return _block==other._block && _entry==other._entry;
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_BLOCKITER_H_
+#define LIBR2C2_BLOCKITER_H_
+
+namespace R2C2 {
+
+class Block;
+class Route;
+class TrackIter;
+
+/**
+An iterator for traversing blocks.
+*/
+class BlockIter
+{
+private:
+ Block *_block;
+ unsigned _entry;
+
+public:
+ BlockIter();
+ BlockIter(Block *, unsigned);
+
+ Block *block() const { return _block; }
+ unsigned entry() const { return _entry; }
+ TrackIter track_iter() const;
+ const Block::Endpoint &endpoint() const;
+
+private:
+ int get_exit(const Route *) const;
+public:
+ BlockIter next(const Route * = 0) const;
+ BlockIter reverse(const Route * = 0) const;
+ BlockIter flip() const;
+
+ Block &operator*() const;
+ Block *operator->() const { return _block; }
+ bool operator==(const BlockIter &) const;
+ bool operator!=(const BlockIter &other) const { return !(*this==other); }
+ operator bool() const { return _block!=0; }
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/core/refptr.h>
+#include <msp/datafile/parser.h>
+#include "catalogue.h"
+#include "tracktype.h"
+#include "vehicletype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+Catalogue::Catalogue():
+ scale(1),
+ gauge(1.524),
+ layout(*this)
+{ }
+
+Catalogue::~Catalogue()
+{
+ for(TrackMap::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ delete i->second;
+ for(VehicleMap::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
+ delete i->second;
+}
+
+float Catalogue::get_rail_elevation() const
+{
+ return ballast_profile.get_height()+rail_profile.get_height();
+}
+
+void Catalogue::add_track(TrackType &track)
+{
+ if(tracks.count(track.get_article_number()))
+ throw Exception("Duplicate track type");
+
+ tracks[track.get_article_number()] = &track;
+ signal_track_added.emit(track);
+}
+
+const TrackType &Catalogue::get_track(const ArticleNumber &art_nr) const
+{
+ TrackMap::const_iterator i=tracks.find(art_nr);
+ if(i==tracks.end())
+ throw KeyError("Unknown track type");
+
+ return *i->second;
+}
+
+void Catalogue::add_vehicle(VehicleType &veh)
+{
+ if(vehicles.count(veh.get_article_number()))
+ throw Exception("Duplicate vehicle type");
+
+ vehicles[veh.get_article_number()] = &veh;
+ signal_vehicle_added.emit(veh);
+}
+
+const VehicleType &Catalogue::get_vehicle(const ArticleNumber &art_nr) const
+{
+ VehicleMap::const_iterator i = vehicles.find(art_nr);
+ if(i==vehicles.end())
+ throw KeyError("Unknown vehicle type");
+
+ return *i->second;
+}
+
+
+Catalogue::Loader::Loader(Catalogue &c):
+ DataFile::BasicLoader<Catalogue>(c)
+{
+ add("ballast_profile", &Loader::ballast_profile);
+ add("gauge", &Loader::gauge);
+ add("layout", &Loader::layout);
+ add("rail_profile", &Loader::rail_profile);
+ add("scale", &Loader::scale);
+ add("track", static_cast<void (Loader::*)(unsigned)>(&Loader::track));
+ add("track", static_cast<void (Loader::*)(ArticleNumber)>(&Loader::track));
+ add("vehicle", static_cast<void (Loader::*)(unsigned)>(&Loader::vehicle));
+ add("vehicle", static_cast<void (Loader::*)(ArticleNumber)>(&Loader::vehicle));
+}
+
+void Catalogue::Loader::ballast_profile()
+{
+ load_sub(obj.ballast_profile);
+}
+
+void Catalogue::Loader::gauge(float g)
+{
+ obj.gauge = g/1000;
+ obj.path_profile = Profile();
+ obj.path_profile.append_point(Point(0.1*obj.gauge, 0));
+ obj.path_profile.append_point(Point(-0.1*obj.gauge, 0));
+}
+
+void Catalogue::Loader::layout()
+{
+ load_sub(obj.layout);
+}
+
+void Catalogue::Loader::rail_profile()
+{
+ load_sub(obj.rail_profile);
+}
+
+void Catalogue::Loader::scale(float n, float d)
+{
+ obj.scale = n/d;
+}
+
+void Catalogue::Loader::track(unsigned art_nr)
+{
+ track(ArticleNumber(art_nr));
+}
+
+void Catalogue::Loader::track(ArticleNumber art_nr)
+{
+ if(obj.tracks.count(art_nr))
+ throw KeyError("Duplicate track type", art_nr.str());
+
+ RefPtr<TrackType> trk = new TrackType(art_nr);
+ load_sub(*trk);
+ obj.add_track(*trk.release());
+}
+
+void Catalogue::Loader::vehicle(unsigned art_nr)
+{
+ vehicle(ArticleNumber(art_nr));
+}
+
+void Catalogue::Loader::vehicle(ArticleNumber art_nr)
+{
+ if(obj.vehicles.count(art_nr))
+ throw KeyError("Duplicate vehicle type", art_nr.str());
+
+ RefPtr<VehicleType> veh = new VehicleType(art_nr);
+ load_sub(*veh);
+ obj.add_vehicle(*veh.release());
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_CATALOGUE_H_
+#define LIBR2C2_CATALOGUE_H_
+
+#include <map>
+#include <msp/datafile/loader.h>
+#include "articlenumber.h"
+#include "layout.h"
+#include "profile.h"
+
+namespace R2C2 {
+
+class TrackType;
+class VehicleType;
+
+class Catalogue
+{
+public:
+ class Loader: public Msp::DataFile::BasicLoader<Catalogue>
+ {
+ public:
+ Loader(Catalogue &);
+ private:
+ void ballast_profile();
+ void gauge(float);
+ void layout();
+ void rail_profile();
+ void scale(float, float);
+ void track(unsigned);
+ void track(ArticleNumber);
+ void vehicle(unsigned);
+ void vehicle(ArticleNumber);
+ };
+
+ typedef std::map<ArticleNumber, TrackType *> TrackMap;
+ typedef std::map<ArticleNumber, VehicleType *> VehicleMap;
+
+ sigc::signal<void, const TrackType &> signal_track_added;
+ sigc::signal<void, const VehicleType &> signal_vehicle_added;
+
+private:
+ float scale;
+ float gauge;
+ Profile rail_profile;
+ Profile ballast_profile;
+ Profile path_profile;
+ TrackMap tracks;
+ VehicleMap vehicles;
+ Layout layout;
+
+public:
+ Catalogue();
+ ~Catalogue();
+
+ float get_scale() const { return scale; }
+ float get_gauge() const { return gauge; }
+ float get_rail_elevation() const;
+ const Profile &get_rail_profile() const { return rail_profile; }
+ const Profile &get_ballast_profile() const { return ballast_profile; }
+ const Profile &get_path_profile() const { return path_profile; }
+
+ void add_track(TrackType &);
+ const TrackType &get_track(const ArticleNumber &) const;
+ const TrackMap &get_tracks() const { return tracks; }
+
+ void add_vehicle(VehicleType &);
+ const VehicleType &get_vehicle(const ArticleNumber &) const;
+ const VehicleMap &get_vehicles() const { return vehicles; }
+
+ Layout &get_layout() { return layout; }
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <cmath>
+#include <msp/core/except.h>
+#include "controller.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+void Controller::Control::set(float v)
+{
+ if(v<min_value)
+ v = min_value;
+ else if(v>max_value)
+ v = max_value;
+ else if(type==BINARY)
+ value = v ? 1 : 0;
+ else if(type==DISCRETE)
+ value = min_value+floor((v-min_value)/step)*step;
+ else if(type==CONTINUOUS)
+ value = v;
+}
+
+Controller::Control Controller::Control::binary(const string &n)
+{
+ Controller::Control tc;
+ tc.name = n;
+ tc.type = BINARY;
+ tc.min_value = 0;
+ tc.max_value = 1;
+ tc.step = 1;
+ tc.value = 0;
+
+ return tc;
+}
+
+Controller::Control Controller::Control::discrete(const string &n, float m, float x, float s)
+{
+ if(x<m)
+ throw InvalidParameterValue("Max value must be greater than min value");
+
+ Controller::Control tc;
+ tc.name = n;
+ tc.type = DISCRETE;
+ tc.min_value = m;
+ tc.max_value = m+floor((x-m)/s)*s;
+ tc.step = s;
+ tc.value = m;
+
+ return tc;
+}
+
+Controller::Control Controller::Control::continuous(const string &n, float m, float x)
+{
+ if(x<m)
+ throw InvalidParameterValue("Max value must be greater than min value");
+
+ Controller::Control tc;
+ tc.name = n;
+ tc.type = CONTINUOUS;
+ tc.min_value = m;
+ tc.max_value = x;
+ tc.step = 0;
+ tc.value = m;
+
+ return tc;
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_CONTROLLER_H_
+#define LIBR2C2_CONTROLLER_H_
+
+#include <string>
+#include <sigc++/signal.h>
+#include <msp/time/timedelta.h>
+
+namespace R2C2 {
+
+/**
+Interface class for train controllers. Takes input through a uniform named
+control interface. Provides information about train movement on output.
+*/
+class Controller
+{
+public:
+ struct Control
+ {
+ enum Type
+ {
+ BINARY,
+ DISCRETE,
+ CONTINUOUS
+ };
+
+ std::string name;
+ Type type;
+ float min_value;
+ float max_value;
+ float step;
+ float value;
+
+ private:
+ Control() { }
+
+ public:
+ void set(float);
+
+ static Control binary(const std::string &);
+ static Control discrete(const std::string &, float, float, float);
+ static Control continuous(const std::string &, float, float);
+ };
+
+ sigc::signal<void, const Control &> signal_control_changed;
+
+protected:
+ Controller() { }
+public:
+ virtual ~Controller() { }
+
+ virtual void set_control(const std::string &, float) = 0;
+ virtual const Control &get_control(const std::string &) const = 0;
+
+ /** Returns the current speed. Always non-negative. */
+ virtual float get_speed() const = 0;
+
+ /** Returns true if traveling in reverse. */
+ virtual bool get_reverse() const = 0;
+
+ /** Determines the distance required to come to a full stop. */
+ virtual float get_braking_distance() const = 0;
+
+ virtual void tick(const Msp::Time::TimeDelta &) = 0;
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/core/except.h>
+#include "driver.h"
+#include "dummy.h"
+#include "intellibox.h"
+
+using namespace std;
+
+namespace R2C2 {
+
+Driver *Driver::create(const string &str)
+{
+ string::size_type colon = str.find(':');
+ string type = str.substr(0, colon);
+ string params;
+
+ if(colon!=string::npos)
+ params = str.substr(colon+1);
+
+ if(type=="ib" || type=="intellibox")
+ return new Intellibox(params);
+ else if(type=="dummy")
+ return new Dummy;
+
+ throw Msp::InvalidParameterValue("Unknown driver");
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_DRIVER_H_
+#define LIBR2C2_DRIVER_H_
+
+#include <string>
+#include <sigc++/signal.h>
+
+namespace R2C2 {
+
+class Driver
+{
+public:
+ sigc::signal<void, bool> signal_power;
+ sigc::signal<void, bool> signal_halt;
+ sigc::signal<void, unsigned, unsigned, bool> signal_loco_speed;
+ sigc::signal<void, unsigned, unsigned, bool> signal_loco_function;
+ sigc::signal<void, unsigned, bool> signal_turnout;
+ sigc::signal<void, unsigned, bool> signal_sensor;
+
+protected:
+ Driver() { }
+public:
+ virtual ~Driver() { }
+
+ virtual void set_power(bool) = 0;
+ virtual bool get_power() const = 0;
+ virtual void halt(bool) = 0;
+ virtual bool is_halted() const = 0;
+
+ virtual const char *enumerate_protocols(unsigned) const = 0;
+ virtual unsigned get_protocol_speed_steps(const std::string &) const = 0;
+ virtual void add_loco(unsigned, const std::string &) = 0;
+ virtual void set_loco_speed(unsigned, unsigned) = 0;
+ virtual void set_loco_reverse(unsigned, bool) = 0;
+ virtual void set_loco_function(unsigned, unsigned, bool) = 0;
+
+ virtual void add_turnout(unsigned) = 0;
+ virtual void set_turnout(unsigned, bool) = 0;
+ virtual bool get_turnout(unsigned) const = 0;
+
+ virtual void add_sensor(unsigned) = 0;
+ virtual void set_sensor(unsigned, bool) = 0;
+ virtual bool get_sensor(unsigned) const = 0;
+
+ virtual void tick() = 0;
+ virtual void flush() = 0;
+
+ static Driver *create(const std::string &);
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include "dummy.h"
+
+using namespace std;
+
+namespace R2C2 {
+
+Dummy::Dummy():
+ power(true)
+{ }
+
+void Dummy::set_power(bool p)
+{
+ power = p;
+ signal_power.emit(power);
+}
+
+const char *Dummy::enumerate_protocols(unsigned i) const
+{
+ if(i==0)
+ return "dummy";
+ return 0;
+}
+
+unsigned Dummy::get_protocol_speed_steps(const string &) const
+{
+ return 0;
+}
+
+void Dummy::add_turnout(unsigned addr)
+{
+ turnouts[addr];
+}
+
+void Dummy::set_turnout(unsigned addr, bool state)
+{
+ if(turnouts[addr]!=state)
+ {
+ turnouts[addr] = state;
+ signal_turnout.emit(addr, state);
+ }
+}
+
+bool Dummy::get_turnout(unsigned addr) const
+{
+ map<unsigned, bool>::const_iterator i = turnouts.find(addr);
+ if(i!=turnouts.end())
+ return i->second;
+ return false;
+}
+
+void Dummy::set_loco_speed(unsigned addr, unsigned speed)
+{
+ LocoState &loco = locos[addr];
+ loco.speed = speed;
+ signal_loco_speed.emit(addr, speed, loco.reverse);
+}
+
+void Dummy::set_loco_reverse(unsigned addr, bool rev)
+{
+ LocoState &loco = locos[addr];
+ loco.reverse = rev;
+ signal_loco_speed.emit(addr, loco.speed, rev);
+}
+
+void Dummy::set_loco_function(unsigned addr, unsigned func, bool state)
+{
+ signal_loco_function.emit(addr, func, state);
+}
+
+void Dummy::set_sensor(unsigned addr, bool state)
+{
+ if(sensors[addr]!=state)
+ {
+ sensors[addr] = state;
+ signal_sensor.emit(addr, state);
+ }
+}
+
+bool Dummy::get_sensor(unsigned addr) const
+{
+ map<unsigned, bool>::const_iterator i = sensors.find(addr);
+ if(i!=sensors.end())
+ return i->second;
+ return false;
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_DUMMY_H_
+#define LIBR2C2_DUMMY_H_
+
+#include <map>
+#include "driver.h"
+
+namespace R2C2 {
+
+class Dummy: public Driver
+{
+private:
+ struct LocoState
+ {
+ unsigned speed;
+ bool reverse;
+ };
+
+ bool power;
+ std::map<unsigned, bool> turnouts;
+ std::map<unsigned, LocoState> locos;
+ std::map<unsigned, bool> sensors;
+
+public:
+ Dummy();
+
+ virtual void set_power(bool);
+ virtual bool get_power() const { return power; }
+ virtual void halt(bool) { }
+ virtual bool is_halted() const { return false; }
+
+ virtual const char *enumerate_protocols(unsigned) const;
+ virtual unsigned get_protocol_speed_steps(const std::string &) const;
+ virtual void add_loco(unsigned, const std::string &) { }
+ virtual void set_loco_speed(unsigned, unsigned);
+ virtual void set_loco_reverse(unsigned, bool);
+ virtual void set_loco_function(unsigned, unsigned, bool);
+
+ virtual void add_turnout(unsigned);
+ virtual void set_turnout(unsigned, bool);
+ virtual bool get_turnout(unsigned) const;
+
+ virtual void add_sensor(unsigned) { }
+ virtual void set_sensor(unsigned, bool);
+ virtual bool get_sensor(unsigned) const;
+
+ virtual void tick() { }
+ virtual void flush() { }
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_GEOMETRY_H_
+#define LIBR2C2_GEOMETRY_H_
+
+#include <cmath>
+#include <vector>
+
+namespace R2C2 {
+
+struct Point
+{
+ float x, y, z;
+
+ Point(): x(0), y(0), z(0) { }
+ Point(float x_, float y_): x(x_), y(y_), z(0) { }
+ Point(float x_, float y_, float z_): x(x_), y(y_), z(z_) { }
+};
+
+inline float distance(const Point &p, const Point &q)
+{ return sqrt((p.x-q.x)*(p.x-q.x) + (p.y-q.y)*(p.y-q.y) + (p.z-q.z)*(p.z-q.z)); }
+
+struct TrackPoint
+{
+ Point pos;
+ float dir;
+ float grade;
+
+ TrackPoint(): dir(0), grade(0) { }
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/poll.h>
+#include <msp/io/print.h>
+#include <msp/time/units.h>
+#include <msp/time/utils.h>
+#include "intellibox.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+Intellibox::Intellibox(const string &dev):
+ power(false),
+ halted(false),
+ update_sensors(false),
+ command_sent(false)
+{
+ serial_fd = ::open(dev.c_str(), O_RDWR);
+ if(serial_fd<0)
+ throw Exception("Couldn't open serial port\n");
+
+ static unsigned baud[]=
+ {
+ 2400, B2400,
+ 4800, B4800,
+ 9600, B9600,
+ 19200, B19200,
+ 0
+ };
+
+ termios attr;
+ tcgetattr(serial_fd, &attr);
+ cfmakeraw(&attr);
+ attr.c_cflag |= CSTOPB;
+
+ bool ok = false;
+ bool p50 = false;
+ for(unsigned i=0; baud[i]; i+=2)
+ {
+ cfsetospeed(&attr, baud[i+1]);
+ tcsetattr(serial_fd, TCSADRAIN, &attr);
+
+ write(serial_fd, "\xC4", 1);
+
+ pollfd pfd = { serial_fd, POLLIN, 0 };
+ if(poll(&pfd, 1, 500)>0)
+ {
+ IO::print("IB detected at %d bits/s\n", baud[i]);
+ char buf[2];
+ p50 = (read(serial_fd, buf, 2)==2);
+ ok = true;
+ break;
+ }
+ }
+
+ if(!ok)
+ throw Exception("IB not detected");
+
+ if(p50)
+ write(serial_fd, "xZzA1\r", 6);
+
+ command(CMD_STATUS);
+}
+
+void Intellibox::set_power(bool p)
+{
+ power = p;
+ if(power)
+ command(CMD_POWER_ON);
+ else
+ command(CMD_POWER_OFF);
+ signal_power.emit(power);
+}
+
+void Intellibox::halt(bool h)
+{
+ halted = h;
+ if(halted)
+ {
+ for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
+ if(i->second.speed)
+ set_loco_speed(i->first, 0);
+ }
+
+ signal_halt.emit(halted);
+}
+
+const char *Intellibox::enumerate_protocols(unsigned i) const
+{
+ if(i==MM)
+ return "MM";
+ else if(i==MM_27)
+ return "MM-27";
+ return 0;
+}
+
+unsigned Intellibox::get_protocol_speed_steps(const string &proto_name) const
+{
+ Protocol proto = map_protocol(proto_name);
+ if(proto==MM)
+ return 14;
+ else if(proto==MM_27)
+ return 27;
+ return 0;
+}
+
+void Intellibox::add_loco(unsigned addr, const string &proto_name)
+{
+ Protocol proto = map_protocol(proto_name);
+
+ if(!locos.count(addr))
+ {
+ locos[addr].protocol = proto;
+
+ unsigned char data[2];
+ data[0] = addr&0xFF;
+ data[1] = (addr>>8)&0xFF;
+ command(CMD_LOK_STATUS, addr, data, 2);
+ }
+}
+
+void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
+{
+ Locomotive &loco = locos[addr];
+ if(speed==loco.speed)
+ {
+ if(loco.pending_half_step)
+ {
+ loco.pending_half_step = 0;
+ loco.half_step_delay = Time::TimeStamp();
+ signal_loco_speed.emit(addr, speed, loco.reverse);
+ }
+ return;
+ }
+ if(speed && halted)
+ return;
+
+ if(loco.protocol==MM_27)
+ {
+ if(speed>27)
+ speed = 27;
+
+ if(speed>loco.speed && !(speed&1))
+ {
+ loco.pending_half_step = -1;
+ speed |= 1;
+ }
+ else if(speed<loco.speed && (speed&1))
+ {
+ loco.pending_half_step = 1;
+ speed &= ~1;
+ }
+ else
+ loco.pending_half_step = 0;
+ loco.half_step_delay = Time::TimeStamp();
+
+ loco_command(addr, (speed+1)/2, loco.reverse, loco.funcs|0x100);
+ }
+ else
+ {
+ if(speed>14)
+ speed = 14;
+
+ loco_command(addr, speed, loco.reverse, loco.funcs|0x100);
+ }
+ loco.speed = speed;
+}
+
+void Intellibox::set_loco_reverse(unsigned addr, bool rev)
+{
+ Locomotive &loco = locos[addr];
+ if(rev==loco.reverse)
+ return;
+
+ loco.reverse = rev;
+ loco_command(addr, loco.speed, rev, loco.funcs|0x100);
+}
+
+void Intellibox::set_loco_function(unsigned addr, unsigned func, bool state)
+{
+ Locomotive &loco = locos[addr];
+ if(state)
+ loco.funcs |= 1<<func;
+ else
+ loco.funcs &= ~(1<<func);
+ loco_command(addr, loco.speed, loco.reverse, loco.funcs);
+ signal_loco_function.emit(addr, func, state);
+}
+
+void Intellibox::add_turnout(unsigned addr)
+{
+ if(!turnouts.count(addr))
+ {
+ turnouts[addr];
+
+ unsigned char data[2];
+ data[0] = addr&0xFF;
+ data[1] = (addr>>8)&0xFF;
+ command(CMD_TURNOUT_STATUS, addr, data, 2);
+ }
+}
+
+void Intellibox::set_turnout(unsigned addr, bool state)
+{
+ Turnout &turnout = turnouts[addr];
+ if(state==turnout.state || state==turnout.pending)
+ return;
+
+ turnout.pending = state;
+ turnout.active = true;
+ turnout.off_timeout = Time::TimeStamp();
+
+ turnout_command(addr, state, true);
+}
+
+bool Intellibox::get_turnout(unsigned addr) const
+{
+ map<unsigned, Turnout>::const_iterator i = turnouts.find(addr);
+ if(i!=turnouts.end())
+ return i->second.state;
+ return false;
+}
+
+void Intellibox::add_sensor(unsigned addr)
+{
+ if(!sensors.count(addr))
+ {
+ sensors[addr];
+ update_sensors = true;
+ }
+}
+
+bool Intellibox::get_sensor(unsigned addr) const
+{
+ map<unsigned, Sensor>::const_iterator i = sensors.find(addr);
+ if(i!=sensors.end())
+ return i->second.state;
+ return false;
+}
+
+void Intellibox::tick()
+{
+ const Time::TimeStamp t = Time::now();
+
+ if(t>next_event_query)
+ {
+ next_event_query = t+200*Time::msec;
+ command(CMD_EVENT);
+ }
+
+ for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
+ if(i->second.protocol==MM_27 && i->second.pending_half_step && i->second.half_step_delay && t>i->second.half_step_delay)
+ {
+ i->second.speed += i->second.pending_half_step;
+ i->second.pending_half_step = 0;
+ i->second.half_step_delay = Time::TimeStamp();
+ loco_command(i->first, (i->second.speed+1)/2, i->second.reverse, i->second.funcs|0x100);
+ }
+
+ for(map<unsigned, Turnout>::iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
+ if(i->second.active && i->second.off_timeout && t>i->second.off_timeout)
+ {
+ i->second.active = false;
+ i->second.off_timeout = Time::TimeStamp();
+ turnout_command(i->first, i->second.state, false);
+ }
+
+ for(map<unsigned, Sensor>::iterator i=sensors.begin(); i!=sensors.end(); ++i)
+ if(i->second.off_timeout && t>i->second.off_timeout)
+ {
+ i->second.state = false;
+ i->second.off_timeout = Time::TimeStamp();
+ signal_sensor.emit(i->first, false);
+ }
+
+ if(update_sensors)
+ {
+ unsigned max_addr = (--sensors.end())->first;
+ unsigned char data[2];
+ data[0] = 0;
+ data[1] = (max_addr+7)/8;
+ command(CMD_SENSOR_PARAM_SET, data, 2);
+ command(CMD_SENSOR_REPORT);
+ update_sensors = false;
+ }
+
+ if(!queue.empty() && command_sent)
+ {
+ pollfd pfd = { serial_fd, POLLIN, 0 };
+ if(poll(&pfd, 1, 0)>0)
+ {
+ process_reply(t);
+ queue.erase(queue.begin());
+ command_sent = false;
+ }
+ else
+ return;
+ }
+
+ if(!queue.empty())
+ {
+ const CommandSlot &slot = queue.front();
+ write(serial_fd, slot.data, slot.length);
+ command_sent = true;
+ }
+}
+
+void Intellibox::flush()
+{
+ Time::TimeStamp t = Time::now();
+ for(list<CommandSlot>::iterator i=queue.begin(); i!=queue.end(); ++i)
+ {
+ write(serial_fd, i->data, i->length);
+ pollfd pfd = { serial_fd, POLLIN, 0 };
+ bool first = true;
+ while(poll(&pfd, 1, (first ? -1 : 0))>0)
+ {
+ char data[16];
+ read(serial_fd, data, 16);
+ first = false;
+ }
+ }
+
+ queue.clear();
+ command_sent = false;
+}
+
+Intellibox::Protocol Intellibox::map_protocol(const string &name) const
+{
+ if(name=="MM")
+ return MM;
+ else if(name=="MM-27")
+ return MM_27;
+ else
+ throw InvalidParameterValue("Unknown protocol");
+}
+
+void Intellibox::command(Command cmd)
+{
+ command(cmd, 0, 0);
+}
+
+void Intellibox::command(Command cmd, const unsigned char *data, unsigned len)
+{
+ command(cmd, 0, data, len);
+}
+
+void Intellibox::command(Command cmd, unsigned addr, const unsigned char *data, unsigned len)
+{
+ CommandSlot slot;
+ slot.cmd = cmd;
+ slot.addr = addr;
+ slot.data[0] = cmd;
+ copy(data, data+len, slot.data+1);
+ slot.length = 1+len;
+ queue.push_back(slot);
+}
+
+void Intellibox::loco_command(unsigned addr, unsigned speed, bool rev, unsigned funcs)
+{
+ unsigned char data[4];
+ data[0] = addr&0xFF;
+ data[1] = (addr>>8)&0xFF;
+
+ if(speed==0)
+ data[2] = 0;
+ else if(speed==1)
+ data[2] = 2;
+ else
+ data[2] = (speed*19-18)/2;
+
+ data[3] = (rev ? 0 : 0x20) | ((funcs&1) ? 0x10 : 0);
+
+ if(!(funcs&0x100))
+ data[3] |= 0x80 | ((funcs>>1)&0xF);
+
+ command(CMD_LOK, addr, data, 4);
+}
+
+void Intellibox::turnout_command(unsigned addr, bool state, bool active)
+{
+ unsigned char data[2];
+ data[0] = addr&0xFF;
+ data[1] = ((addr>>8)&0x7) | (active ? 0x40 : 0) | (state ? 0x80 : 0);
+ command(CMD_TURNOUT, addr, data, 2);
+}
+
+void Intellibox::process_reply(const Time::TimeStamp &t)
+{
+ Command cmd = queue.front().cmd;
+
+ if(cmd==CMD_STATUS)
+ {
+ unsigned char status;
+ read_all(&status, 1);
+ power = status&0x08;
+ signal_power.emit(power);
+ }
+ else if(cmd==CMD_EVENT)
+ {
+ for(unsigned i=0;; ++i)
+ {
+ unsigned char byte;
+ read_all(&byte, 1);
+
+ if(i==0)
+ {
+ if(byte&0x01)
+ command(CMD_EVENT_LOK);
+ if(byte&0x20)
+ command(CMD_EVENT_TURNOUT);
+ if(byte&0x04)
+ command(CMD_EVENT_SENSOR);
+ }
+ else if(i==1)
+ {
+ if(byte&0x40)
+ command(CMD_STATUS);
+ }
+
+ if(!(byte&0x80))
+ break;
+ }
+ }
+ else if(cmd==CMD_EVENT_LOK)
+ {
+ while(1)
+ {
+ unsigned char data[5];
+ read_all(data, 1);
+ if(data[0]==0x80)
+ break;
+ read_all(data+1, 4);
+ }
+ }
+ else if(cmd==CMD_EVENT_TURNOUT)
+ {
+ unsigned char count;
+ read_all(&count, 1);
+ for(unsigned i=0; i<count; ++i)
+ {
+ unsigned char data[2];
+ read_all(data, 2);
+
+ unsigned addr = data[0]+((data[1]&7)<<8);
+ Turnout &turnout = turnouts[addr];
+ turnout.state = (data[1]&0x80)!=0;
+ turnout.pending = turnout.state;
+ signal_turnout.emit(addr, turnout.state);
+ }
+ }
+ else if(cmd==CMD_EVENT_SENSOR)
+ {
+ while(1)
+ {
+ unsigned char mod;
+ read_all(&mod, 1);
+ if(!mod)
+ break;
+
+ unsigned char data[2];
+ read_all(data, 2);
+ for(unsigned i=0; i<16; ++i)
+ {
+ unsigned addr = mod*16+i-15;
+ bool state = (data[i/8]>>(7-i%8))&1;
+
+ Sensor &sensor = sensors[addr];
+ if(state)
+ {
+ sensor.off_timeout = Time::TimeStamp();
+ if(!sensor.state)
+ {
+ sensor.state = state;
+ signal_sensor(addr, state);
+ }
+ }
+ else if(sensor.state)
+ sensor.off_timeout = t+700*Time::msec;
+ }
+ }
+ }
+ else if(cmd==CMD_LOK)
+ {
+ Error err;
+ read_status(&err);
+
+ if(err==ERR_NO_ERROR)
+ {
+ unsigned addr = queue.front().addr;
+ Locomotive &loco = locos[addr];
+ signal_loco_speed.emit(addr, loco.speed+loco.pending_half_step, loco.reverse);
+ if(loco.pending_half_step)
+ loco.half_step_delay = Time::now()+500*Time::msec;
+ }
+ else
+ error(cmd, err);
+ }
+ else if(cmd==CMD_TURNOUT)
+ {
+ Error err;
+ read_status(&err);
+
+ unsigned addr = queue.front().addr;
+ Turnout &turnout = turnouts[addr];
+
+ if(err==ERR_NO_ERROR)
+ {
+ turnout.state = turnout.pending;
+ if(turnout.active)
+ {
+ signal_turnout.emit(addr, turnout.state);
+ turnout.off_timeout = t+500*Time::msec;
+ }
+ }
+ else if(err==ERR_NO_I2C_SPACE)
+ queue.push_back(queue.front());
+ else
+ {
+ turnout.pending = turnout.state;
+ error(cmd, err);
+ }
+ }
+ else if(cmd==CMD_TURNOUT_STATUS)
+ {
+ Error err;
+ read_status(&err);
+
+ if(err==ERR_NO_ERROR)
+ {
+ unsigned char data;
+ read_all(&data, 1);
+
+ unsigned addr = queue.front().addr;
+ bool state = data&0x04;
+
+ Turnout &turnout = turnouts[addr];
+ if(state!=turnout.state)
+ {
+ turnout.state = state;
+ turnout.pending = state;
+ signal_turnout.emit(addr, turnout.state);
+ }
+ }
+ else
+ error(cmd, err);
+ }
+ else if(cmd==CMD_LOK_STATUS)
+ {
+ Error err;
+ read_status(&err);
+
+ if(err==ERR_NO_ERROR)
+ {
+ unsigned char data[3];
+ read_all(data, 3);
+
+ unsigned addr = queue.front().addr;
+ Locomotive &loco = locos[addr];
+
+ unsigned speed = (data[0]<=1 ? 0 : data[0]*2/19+1);
+ bool reverse = !(data[1]&0x20);
+ if(speed!=loco.speed || reverse!=loco.reverse)
+ {
+ loco.speed = speed;
+ loco.reverse = reverse;
+ signal_loco_speed.emit(addr, loco.speed, loco.reverse);
+ }
+
+ unsigned funcs = (data[1]&0xF)<<1;
+ if(data[1]&0x10)
+ funcs |= 1;
+ if(funcs!=loco.funcs)
+ {
+ unsigned changed = loco.funcs^funcs;
+ loco.funcs = funcs;
+ for(unsigned i=0; i<5; ++i)
+ if(changed&(1<<i))
+ signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
+ }
+ }
+ else
+ error(cmd, err);
+ }
+ else
+ {
+ unsigned expected_bytes = 0;
+ if(cmd==CMD_FUNC_STATUS)
+ expected_bytes = 1;
+ if(cmd==CMD_TURNOUT_GROUP_STATUS)
+ expected_bytes = 2;
+ if(cmd==CMD_LOK_CONFIG)
+ expected_bytes = 4;
+
+ Error err;
+ read_status(&err);
+
+ if(err==ERR_NO_ERROR)
+ {
+ unsigned char data[8];
+ read_all(data, expected_bytes);
+ }
+ else
+ error(cmd, err);
+ }
+}
+
+unsigned Intellibox::read_all(unsigned char *buf, unsigned len)
+{
+ unsigned pos = 0;
+ while(pos<len)
+ pos += read(serial_fd, buf+pos, len-pos);
+
+ return pos;
+}
+
+unsigned Intellibox::read_status(Error *err)
+{
+ unsigned char c;
+ unsigned ret = read_all(&c, 1);
+ *err = static_cast<Error>(c);
+ return ret;
+}
+
+void Intellibox::error(Command cmd, Error err)
+{
+ const char *cmd_str = 0;
+ switch(cmd)
+ {
+ case CMD_LOK: cmd_str = "CMD_LOK"; break;
+ case CMD_LOK_STATUS: cmd_str = "CMD_LOK_STATUS"; break;
+ case CMD_LOK_CONFIG: cmd_str = "CMD_LOK_CONFIG"; break;
+ case CMD_FUNC: cmd_str = "CMD_FUNC"; break;
+ case CMD_FUNC_STATUS: cmd_str = "CMD_FUNC_STATUS"; break;
+ case CMD_TURNOUT: cmd_str = "CMD_TURNOUT"; break;
+ case CMD_TURNOUT_FREE: cmd_str = "CMD_TURNOUT_FREE"; break;
+ case CMD_TURNOUT_STATUS: cmd_str = "CMD_TURNOUT_STATUS"; break;
+ case CMD_TURNOUT_GROUP_STATUS: cmd_str = "CMD_TURNOUT_GROUP_STATUS"; break;
+ case CMD_SENSOR_STATUS: cmd_str = "CMD_SENSOR_STATUS"; break;
+ case CMD_SENSOR_REPORT: cmd_str = "CMD_SENSOR_REPORT"; break;
+ case CMD_SENSOR_PARAM_SET: cmd_str = "CMD_SENSOR_PARAM_SET"; break;
+ case CMD_STATUS: cmd_str = "CMD_STATUS"; break;
+ case CMD_POWER_OFF: cmd_str = "CMD_POWER_OFF"; break;
+ case CMD_POWER_ON: cmd_str = "CMD_POWER_ON"; break;
+ case CMD_NOP: cmd_str = "CMD_NOP"; break;
+ case CMD_EVENT: cmd_str = "CMD_EVENT"; break;
+ case CMD_EVENT_LOK: cmd_str = "CMD_EVENT_LOK"; break;
+ case CMD_EVENT_TURNOUT: cmd_str = "CMD_EVENT_TURNOUT"; break;
+ case CMD_EVENT_SENSOR: cmd_str = "CMD_EVENT_SENSOR"; break;
+ default: cmd_str = "(unknown command)";
+ }
+
+ const char *err_str = 0;
+ switch(err)
+ {
+ case ERR_NO_ERROR: err_str = "ERR_NO_ERROR"; break;
+ case ERR_SYS_ERROR: err_str = "ERR_SYS_ERROR"; break;
+ case ERR_BAD_PARAM: err_str = "ERR_BAD_PARAM"; break;
+ case ERR_POWER_OFF: err_str = "ERR_POWER_OFF"; break;
+ case ERR_NO_LOK_SPACE: err_str = "ERR_NO_LOK_SPACE"; break;
+ case ERR_NO_TURNOUT_SPACE: err_str = "ERR_NO_TURNOUT_SPACE"; break;
+ case ERR_NO_DATA: err_str = "ERR_NO_DATA"; break;
+ case ERR_NO_SLOT: err_str = "ERR_NO_SLOT"; break;
+ case ERR_BAD_LOK_ADDR: err_str = "ERR_BAD_LOK_ADDR"; break;
+ case ERR_LOK_BUSY: err_str = "ERR_LOK_BUSY"; break;
+ case ERR_BAD_TURNOUT_ADDR: err_str = "ERR_BAD_TURNOUT_ADDR"; break;
+ case ERR_BAD_SO_VALUE: err_str = "ERR_BAD_SO_VALUE"; break;
+ case ERR_NO_I2C_SPACE: err_str = "ERR_NO_I2C_SPACE"; break;
+ case ERR_LOW_TURNOUT_SPACE: err_str = "ERR_LOW_TURNOUT_SPACE"; break;
+ case ERR_LOK_HALTED: err_str = "ERR_LOK_HALTED"; break;
+ case ERR_LOK_POWER_OFF: err_str = "ERR_LOK_POWER_OFF"; break;
+ default: cmd_str = "(unknown error)";
+ }
+
+ IO::print("Error: %s: %s\n", cmd_str, err_str);
+}
+
+
+Intellibox::Locomotive::Locomotive():
+ speed(0),
+ reverse(false),
+ funcs(0)
+{ }
+
+
+Intellibox::Turnout::Turnout():
+ state(false),
+ active(false),
+ pending(false)
+{ }
+
+
+Intellibox::Sensor::Sensor():
+ state(false)
+{ }
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_INTELLIBOX_H_
+#define LIBR2C2_INTELLIBOX_H_
+
+#include <map>
+#include <msp/time/timestamp.h>
+#include "driver.h"
+
+namespace R2C2 {
+
+/**
+Driver for Uhlenbrock Intellibox. Uses the P50X binary protocol over RS232.
+
+Motorola decoders with 27 speed steps are supported by manually generating the
+commands necessary to reach the "half-steps". However, sending a rapid stream
+of speed changes to the same locomotive seems to cause excessive lag, so we
+cheat a bit; instead of sending the half-step command immediately, we send it
+with a 500ms delay, but only if no new set_loco_speed calls have occurred. As
+a downside from this accelerations and decelerations are still jerky.
+*/
+class Intellibox: public Driver
+{
+private:
+ enum Command
+ {
+ CMD_LOK=0x80,
+ CMD_LOK_STATUS=0x84,
+ CMD_LOK_CONFIG=0x85,
+ CMD_FUNC=0x88,
+ CMD_FUNC_STATUS=0x8C,
+ CMD_TURNOUT=0x90,
+ CMD_TURNOUT_FREE=0x93,
+ CMD_TURNOUT_STATUS=0x94,
+ CMD_TURNOUT_GROUP_STATUS=0x95,
+ CMD_SENSOR_STATUS=0x98,
+ CMD_SENSOR_REPORT=0x99,
+ CMD_SENSOR_PARAM_SET=0x9D,
+ CMD_STATUS=0xA2,
+ CMD_POWER_OFF=0xA6,
+ CMD_POWER_ON=0xA7,
+ CMD_NOP=0xC4,
+ CMD_EVENT=0xC8,
+ CMD_EVENT_LOK=0xC9,
+ CMD_EVENT_TURNOUT=0xCA,
+ CMD_EVENT_SENSOR=0xCB
+ };
+
+ enum Error
+ {
+ ERR_NO_ERROR=0,
+ ERR_SYS_ERROR,
+ ERR_BAD_PARAM,
+ ERR_POWER_OFF=0x6,
+ ERR_NO_LOK_SPACE=0x8, // No space in lok command buffer
+ ERR_NO_TURNOUT_SPACE, // No space in turnout command buffer
+ ERR_NO_DATA, // "no Lok status available (Lok is not in a slot)"
+ ERR_NO_SLOT, // "there is no slot available"
+ ERR_BAD_LOK_ADDR,
+ ERR_LOK_BUSY,
+ ERR_BAD_TURNOUT_ADDR,
+ ERR_BAD_SO_VALUE,
+ ERR_NO_I2C_SPACE,
+ ERR_LOW_TURNOUT_SPACE=0x40,
+ ERR_LOK_HALTED,
+ ERR_LOK_POWER_OFF,
+ };
+
+ enum Protocol
+ {
+ MM,
+ MM_27
+ };
+
+ struct Locomotive
+ {
+ Protocol protocol;
+ unsigned speed;
+ bool reverse;
+ unsigned funcs;
+ int pending_half_step;
+ Msp::Time::TimeStamp half_step_delay;
+
+ Locomotive();
+ };
+
+ struct Turnout
+ {
+ bool state;
+ bool active;
+ bool pending;
+ Msp::Time::TimeStamp off_timeout;
+
+ Turnout();
+ };
+
+ struct Sensor
+ {
+ bool state;
+ Msp::Time::TimeStamp off_timeout;
+
+ Sensor();
+ };
+
+ struct CommandSlot
+ {
+ Command cmd;
+ unsigned addr;
+ unsigned char data[8];
+ unsigned length;
+ };
+
+ int serial_fd;
+ bool power;
+ bool halted;
+ std::map<unsigned, Locomotive> locos;
+ std::map<unsigned, Turnout> turnouts;
+ std::map<unsigned, Sensor> sensors;
+ bool update_sensors;
+ std::list<CommandSlot> queue;
+ bool command_sent;
+ Msp::Time::TimeStamp next_event_query;
+
+public:
+ Intellibox(const std::string &);
+
+ virtual void set_power(bool);
+ virtual bool get_power() const { return power; }
+ virtual void halt(bool);
+ virtual bool is_halted() const { return halted; }
+
+ virtual const char *enumerate_protocols(unsigned) const;
+ virtual unsigned get_protocol_speed_steps(const std::string &) const;
+ virtual void add_loco(unsigned, const std::string &);
+ virtual void set_loco_speed(unsigned, unsigned);
+ virtual void set_loco_reverse(unsigned, bool);
+ virtual void set_loco_function(unsigned, unsigned, bool);
+
+ virtual void add_turnout(unsigned);
+ virtual void set_turnout(unsigned, bool);
+ virtual bool get_turnout(unsigned) const;
+
+ virtual void add_sensor(unsigned);
+ virtual void set_sensor(unsigned, bool) { }
+ virtual bool get_sensor(unsigned) const;
+
+ virtual void tick();
+ virtual void flush();
+
+private:
+ Protocol map_protocol(const std::string &) const;
+ void command(Command);
+ void command(Command, const unsigned char *, unsigned);
+ void command(Command, unsigned, const unsigned char *, unsigned);
+ void loco_command(unsigned, unsigned, bool, unsigned);
+ void turnout_command(unsigned, bool, bool);
+ void process_reply(const Msp::Time::TimeStamp &);
+ unsigned read_all(unsigned char *, unsigned);
+ unsigned read_status(Error *);
+ void error(Command, Error);
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <algorithm>
+#include <msp/core/refptr.h>
+#include <msp/datafile/parser.h>
+#include <msp/datafile/writer.h>
+#include <msp/io/print.h>
+#include <msp/time/utils.h>
+#include "block.h"
+#include "catalogue.h"
+#include "driver.h"
+#include "layout.h"
+#include "route.h"
+#include "track.h"
+#include "tracktype.h"
+#include "train.h"
+#include "vehicletype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+Layout::Layout(Catalogue &c, Driver *d):
+ catalogue(c),
+ driver(d),
+ next_turnout_id(0x800)
+{
+ if(driver)
+ driver->signal_sensor.connect(sigc::mem_fun(this, &Layout::sensor_event));
+}
+
+Layout::~Layout()
+{
+ delete driver;
+ while(!trains.empty())
+ delete trains.begin()->second;
+ while(!routes.empty())
+ delete *routes.begin();
+ while(!tracks.empty())
+ delete *tracks.begin();
+ while(!blocks.empty())
+ delete *blocks.begin();
+}
+
+Driver &Layout::get_driver() const
+{
+ if(!driver)
+ throw InvalidState("No driver");
+ return *driver;
+}
+
+void Layout::add_track(Track &t)
+{
+ if(tracks.insert(&t).second)
+ {
+ create_blocks();
+ signal_track_added.emit(t);
+ }
+}
+
+void Layout::remove_track(Track &t)
+{
+ if(tracks.erase(&t))
+ {
+ create_blocks(t);
+ signal_track_removed.emit(t);
+ }
+}
+
+unsigned Layout::allocate_turnout_id(bool dbl)
+{
+ set<unsigned> used_ids;
+ for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ if((*i)->get_turnout_id())
+ used_ids.insert((*i)->get_turnout_id());
+
+ unsigned result = next_turnout_id;
+ while(used_ids.count(result) || (dbl && used_ids.count(result+1)))
+ ++result;
+ next_turnout_id = result+1+dbl;
+
+ return result;
+}
+
+void Layout::add_block(Block &b)
+{
+ blocks.insert(&b);
+}
+
+Block &Layout::get_block(unsigned id) const
+{
+ for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ if((*i)->get_id()==id)
+ return **i;
+
+ throw KeyError("Unknown block", lexical_cast(id));
+}
+
+void Layout::create_blocks()
+{
+ set<Track *> used_tracks;
+ for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ {
+ const set<Track *> &btracks = (*i)->get_tracks();
+ used_tracks.insert(btracks.begin(), btracks.end());
+ }
+
+ for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ if(used_tracks.count(*i)==0)
+ {
+ Block *block = new Block(*this, **i);
+ used_tracks.insert(block->get_tracks().begin(), block->get_tracks().end());
+ }
+
+ for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ for(set<Block *>::iterator j=i; j!=blocks.end(); ++j)
+ if(j!=i)
+ (*i)->check_link(**j);
+}
+
+void Layout::create_blocks(Track &track)
+{
+ /* Must collect the blocks in a set first while all tracks are still
+ guaranteed to have blocks and to avoid duplicate deletes */
+ set<Block *> del_blocks;
+
+ del_blocks.insert(&track.get_block());
+
+ const vector<Track *> &links = track.get_links();
+ for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
+ if(*i)
+ del_blocks.insert(&(*i)->get_block());
+
+ for(set<Block *>::iterator i=del_blocks.begin(); i!=del_blocks.end(); ++i)
+ delete *i;
+
+ create_blocks();
+}
+
+void Layout::remove_block(Block &b)
+{
+ blocks.erase(&b);
+}
+
+void Layout::add_route(Route &r)
+{
+ if(routes.insert(&r).second)
+ signal_route_added.emit(r);
+}
+
+Route &Layout::get_route(const string &name) const
+{
+ for(set<Route *>::const_iterator i=routes.begin(); i!=routes.end(); ++i)
+ if((*i)->get_name()==name)
+ return **i;
+ throw KeyError("Unknown route", name);
+}
+
+void Layout::update_routes()
+{
+ for(set<Route *>::iterator i=routes.begin(); i!=routes.end(); ++i)
+ (*i)->update_turnouts();
+}
+
+void Layout::remove_route(Route &r)
+{
+ if(routes.erase(&r))
+ signal_route_removed.emit(r);
+}
+
+void Layout::add_train(Train &t)
+{
+ if(trains.count(t.get_address()))
+ throw KeyError("Duplicate train address", lexical_cast(t.get_address()));
+
+ trains[t.get_address()] = &t;
+ signal_train_added.emit(t);
+}
+
+Train &Layout::get_train(unsigned addr) const
+{
+ map<unsigned, Train *>::const_iterator i = trains.find(addr);
+ if(i==trains.end())
+ throw KeyError("Unknown train", lexical_cast(addr));
+ return *i->second;
+}
+
+void Layout::remove_train(Train &t)
+{
+ if(trains.erase(t.get_address()))
+ signal_train_removed.emit(t);
+}
+
+void Layout::add_vehicle(Vehicle &v)
+{
+ if(vehicles.insert(&v).second)
+ signal_vehicle_added.emit(v);
+}
+
+void Layout::remove_vehicle(Vehicle &v)
+{
+ if(vehicles.erase(&v))
+ signal_vehicle_removed.emit(v);
+}
+
+void Layout::tick()
+{
+ if(driver)
+ driver->tick();
+
+ Time::TimeStamp t = Time::now();
+ Time::TimeDelta dt;
+ if(last_tick)
+ dt = t-last_tick;
+ last_tick = t;
+
+ for(map<unsigned, Train *>::iterator i=trains.begin(); i!=trains.end(); ++i)
+ i->second->tick(t, dt);
+}
+
+void Layout::emergency(const string &msg)
+{
+ if(driver)
+ driver->halt(true);
+ IO::print("Emergency: %s\n", msg);
+ signal_emergency.emit(msg);
+}
+
+void Layout::save(const string &fn)
+{
+ IO::BufferedFile out(fn, IO::M_WRITE);
+ DataFile::Writer writer(out);
+
+ if(!base.empty())
+ writer.write((DataFile::Statement("base"), base));
+
+ for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ {
+ DataFile::Statement st("track");
+ st.append((*i)->get_type().get_article_number());
+ (*i)->save(st.sub);
+ writer.write(st);
+ }
+
+ for(set<Route *>::iterator i=routes.begin(); i!=routes.end(); ++i)
+ {
+ if((*i)->is_temporary())
+ continue;
+
+ DataFile::Statement st("route");
+ (*i)->save(st.sub);
+ writer.write(st);
+ }
+}
+
+void Layout::save_trains(const string &fn)
+{
+ IO::BufferedFile out(fn, IO::M_WRITE);
+ DataFile::Writer writer(out);
+
+ for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
+ {
+ DataFile::Statement st("train");
+ st.append(i->second->get_locomotive_type().get_article_number());
+ st.append(i->second->get_address());
+ st.append(i->second->get_protocol());
+ i->second->save(st.sub);
+ writer.write(st);
+ }
+}
+
+void Layout::sensor_event(unsigned addr, bool state)
+{
+ if(state)
+ {
+ for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ if((*i)->get_sensor_id()==addr)
+ {
+ if(!(*i)->get_train())
+ emergency(format("Unreserved sensor %d triggered", addr));
+ break;
+ }
+ }
+}
+
+
+Layout::Loader::Loader(Layout &l):
+ DataFile::BasicLoader<Layout>(l),
+ new_tracks(false)
+{
+ add("base", &Layout::base);
+ add("route", static_cast<void (Loader::*)()>(&Loader::route));
+ add("route", static_cast<void (Loader::*)(const string &)>(&Loader::route));
+ add("track", static_cast<void (Loader::*)(unsigned)>(&Loader::track));
+ add("track", static_cast<void (Loader::*)(ArticleNumber)>(&Loader::track));
+ add("train", static_cast<void (Loader::*)(unsigned, unsigned, const std::string &)>(&Loader::train));
+ add("train", static_cast<void (Loader::*)(ArticleNumber, unsigned, const std::string &)>(&Loader::train));
+}
+
+void Layout::Loader::finish()
+{
+ for(set<Track *>::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i)
+ (*i)->check_slope();
+}
+
+void Layout::Loader::route()
+{
+ Route *rte = new Route(obj);
+ load_sub(*rte);
+}
+
+void Layout::Loader::route(const string &n)
+{
+ Route *rte = new Route(obj);
+ rte->set_name(n);
+ load_sub(*rte);
+}
+
+void Layout::Loader::track(unsigned art_nr)
+{
+ track(ArticleNumber(art_nr));
+}
+
+void Layout::Loader::track(ArticleNumber art_nr)
+{
+ Track *trk = new Track(obj, obj.catalogue.get_track(art_nr));
+ load_sub(*trk);
+ new_tracks = true;
+ for(set<Track *>::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i)
+ if(*i!=trk)
+ trk->snap_to(**i, true);
+}
+
+void Layout::Loader::train(unsigned art_nr, unsigned addr, const std::string &proto)
+{
+ train(ArticleNumber(art_nr), addr, proto);
+}
+
+void Layout::Loader::train(ArticleNumber art_nr, unsigned addr, const std::string &proto)
+{
+ Train *trn = new Train(obj, obj.catalogue.get_vehicle(art_nr), addr, proto);
+ load_sub(*trn);
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_LAYOUT_H_
+#define LIBR2C2_LAYOUT_H_
+
+#include <set>
+#include <sigc++/sigc++.h>
+#include <msp/datafile/loader.h>
+#include <msp/time/timestamp.h>
+
+namespace R2C2 {
+
+class ArticleNumber;
+class Block;
+class Catalogue;
+class Driver;
+class Route;
+class Track;
+class Train;
+class Vehicle;
+
+class Layout
+{
+public:
+ class Loader: public Msp::DataFile::BasicLoader<Layout>
+ {
+ private:
+ bool new_tracks;
+
+ public:
+ Loader(Layout &);
+ private:
+ virtual void finish();
+ void route();
+ void route(const std::string &);
+ void track(unsigned);
+ void track(ArticleNumber);
+ void train(unsigned, unsigned, const std::string &);
+ void train(ArticleNumber, unsigned, const std::string &);
+ };
+
+public:
+ sigc::signal<void, Track &> signal_track_added;
+ sigc::signal<void, Track &> signal_track_removed;
+ sigc::signal<void, Route &> signal_route_added;
+ sigc::signal<void, Route &> signal_route_removed;
+ sigc::signal<void, Train &> signal_train_added;
+ sigc::signal<void, Train &> signal_train_removed;
+ sigc::signal<void, Vehicle &> signal_vehicle_added;
+ sigc::signal<void, Vehicle &> signal_vehicle_removed;
+ sigc::signal<void, Block &, Train *> signal_block_reserved;
+ sigc::signal<void, const std::string &> signal_emergency;
+
+private:
+ Catalogue &catalogue;
+ Driver *driver;
+ std::string base;
+ std::set<Track *> tracks;
+ std::set<Route *> routes;
+ std::set<Block *> blocks;
+ std::map<unsigned, Train *> trains;
+ std::set<Vehicle *> vehicles;
+ Msp::Time::TimeStamp last_tick;
+ unsigned next_turnout_id;
+
+public:
+ Layout(Catalogue &, Driver * = 0);
+ ~Layout();
+
+ Catalogue &get_catalogue() const { return catalogue; }
+ bool has_driver() const { return driver; }
+ Driver &get_driver() const;
+ const std::string &get_base() const { return base; }
+
+ void add_track(Track &);
+ const std::set<Track *> &get_tracks() const { return tracks; }
+ void remove_track(Track &);
+ unsigned allocate_turnout_id(bool);
+
+ void add_block(Block &);
+ Block &get_block(unsigned) const;
+ const std::set<Block *> &get_blocks() const { return blocks; }
+ void create_blocks();
+ void create_blocks(Track &);
+ void remove_block(Block &);
+
+ void add_route(Route &);
+ const std::set<Route *> &get_routes() const { return routes; }
+ Route &get_route(const std::string &) const;
+ void update_routes();
+ void remove_route(Route &);
+
+ void add_train(Train &);
+ Train &get_train(unsigned) const;
+ const std::map<unsigned, Train *> &get_trains() const { return trains; }
+ void remove_train(Train &);
+
+ void add_vehicle(Vehicle &);
+ void remove_vehicle(Vehicle &);
+
+ void tick();
+ void emergency(const std::string &);
+
+ void save(const std::string &);
+ void save_trains(const std::string &);
+private:
+ void sensor_event(unsigned, bool);
+};
+
+} // namespace R2C2
+
+#endif
+
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <cmath>
+#include "profile.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+void Profile::append_point(const Point &p)
+{
+ points.push_back(p);
+ if(points.size()==1)
+ {
+ min_coords = p;
+ max_coords = p;
+ }
+ else
+ {
+ min_coords.x = min(min_coords.x, p.x);
+ min_coords.y = min(min_coords.y, p.y);
+ max_coords.x = max(max_coords.x, p.x);
+ max_coords.y = max(max_coords.y, p.y);
+ }
+}
+
+const Point &Profile::get_point(unsigned i) const
+{
+ if(i>=points.size())
+ throw InvalidParameterValue("Index out of range");
+ return points[i];
+}
+
+Point Profile::get_edge_normal(unsigned i) const
+{
+ if(i+1>=points.size())
+ throw InvalidParameterValue("Index out of range");
+ float dx = points[i+1].x-points[i].x;
+ float dy = points[i+1].y-points[i].y;
+ float len = sqrt(dx*dx+dy*dy);
+ return Point(dy/len, -dx/len);
+}
+
+
+Profile::Loader::Loader(Profile &p):
+ DataFile::ObjectLoader<Profile>(p)
+{
+ add("point", &Loader::point);
+}
+
+void Profile::Loader::point(float x, float y)
+{
+ obj.append_point(Point(x/1000, y/1000));
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_PROFILE_H_
+#define LIBR2C2_PROFILE_H_
+
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include "geometry.h"
+
+namespace R2C2 {
+
+class Profile
+{
+public:
+ class Loader: public Msp::DataFile::ObjectLoader<Profile>
+ {
+ public:
+ Loader(Profile &);
+ private:
+ void point(float, float);
+ };
+
+private:
+ std::vector<Point> points;
+ Point min_coords;
+ Point max_coords;
+
+public:
+ void append_point(const Point &);
+ unsigned get_n_points() const { return points.size(); }
+ const Point &get_point(unsigned) const;
+ const Point &get_min_coords() const { return min_coords; }
+ const Point &get_max_coords() const { return max_coords; }
+ float get_width() const { return max_coords.x-min_coords.x; }
+ float get_height() const { return max_coords.y-min_coords.y; }
+ Point get_edge_normal(unsigned) const;
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2007-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <queue>
+#include <msp/strings/formatter.h>
+#include "layout.h"
+#include "route.h"
+#include "track.h"
+#include "trackiter.h"
+#include "tracktype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace {
+
+using namespace R2C2;
+
+typedef std::pair<Track *, unsigned> Key;
+
+struct Node
+{
+ TrackIter track;
+ Node *prev;
+ float dist;
+
+ Node():
+ prev(0), dist(0)
+ { }
+
+ Node(const TrackIter &t):
+ track(t), prev(0), dist(0)
+ { }
+
+ Node(const TrackIter &t, Node &r, float d):
+ track(t), prev(&r), dist(prev->dist+d)
+ { }
+
+ bool operator<(const Node &other) const
+ { return dist>other.dist; }
+};
+
+struct TrackMatch
+{
+ Track &track;
+
+ TrackMatch(Track &t): track(t) { }
+
+ bool operator()(Track &t) const { return &t==&track; }
+};
+
+struct TrackInSet
+{
+ const set<Track *> &tracks;
+
+ TrackInSet(const set<Track *> &t): tracks(t) { }
+
+ bool operator()(Track &t) const { return tracks.count(&t); }
+};
+
+template<typename Pred>
+list<Track *> dijkstra(const TrackIter &from, const Pred &goal)
+{
+ map<Key, Node> track_nodes;
+ priority_queue<Node> nodes;
+ Node *final = 0;
+
+ nodes.push(from);
+
+ while(!nodes.empty())
+ {
+ Node lowest = nodes.top();
+ nodes.pop();
+
+ Key key(lowest.track.track(), lowest.track.entry());
+ if(track_nodes.count(key))
+ continue;
+
+ Node &ref = track_nodes[key] = lowest;
+ if(goal(*lowest.track))
+ {
+ final = &ref;
+ break;
+ }
+
+ unsigned paths = lowest.track.endpoint().paths;
+ for(unsigned i=0; paths>>i; ++i)
+ if(paths&(1<<i))
+ {
+ TrackIter next = lowest.track.next(i);
+ if(!next)
+ continue;
+
+ if(track_nodes.count(Key(next.track(), next.entry())))
+ continue;
+
+ nodes.push(Node(next, ref, lowest.track->get_type().get_path_length(i)));
+ }
+ }
+
+ list<Track *> result;
+ for(Node *node=final; node; node=node->prev)
+ result.push_front(&*node->track);
+
+ return result;
+}
+
+template<typename Pred>
+Route *create_route(const TrackIter &from, const Pred &goal)
+{
+ list<Track *> tracks = dijkstra(from, goal);
+
+ if(tracks.empty())
+ return 0;
+
+ Route *route = new Route(from->get_layout());
+ for(list<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ route->add_track(**i);
+
+ route->set_name("Pathfinder");
+ route->set_temporary(true);
+
+ return route;
+}
+
+}
+
+
+namespace R2C2 {
+
+Route::Route(Layout &l):
+ layout(l),
+ temporary(false)
+{
+ layout.add_route(*this);
+ layout.signal_track_removed.connect(sigc::mem_fun(this, &Route::track_removed));
+}
+
+Route::~Route()
+{
+ layout.remove_route(*this);
+}
+
+void Route::set_name(const string &n)
+{
+ name = n;
+ signal_name_changed.emit(name);
+}
+
+void Route::set_temporary(bool t)
+{
+ temporary = t;
+}
+
+void Route::set_turnout(unsigned addr, unsigned path)
+{
+ if(!addr)
+ throw InvalidParameterValue("Invalid turnout address");
+ map<unsigned, int>::iterator i = turnouts.find(addr);
+ if(i==turnouts.end())
+ throw KeyError("Turnout is not in this route");
+ if(i->second>=0 && path!=static_cast<unsigned>(i->second))
+ throw InvalidState("Setting conflicts with route");
+ i->second = path;
+}
+
+void Route::update_turnouts()
+{
+ set<unsigned> found;
+ for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ if(unsigned tid = (*i)->get_turnout_id())
+ {
+ found.insert(tid);
+
+ const vector<TrackType::Endpoint> &endpoints = (*i)->get_type().get_endpoints();
+ const vector<Track *> &links = (*i)->get_links();
+
+ // Build a combined path mask from linked endpoints
+ unsigned mask = (*i)->get_type().get_paths();
+ for(unsigned j=0; j<endpoints.size(); ++j)
+ {
+ if(!tracks.count(links[j]))
+ continue;
+
+ if(unsigned tid2 = links[j]->get_turnout_id())
+ {
+ const TrackType::Endpoint &ep = links[j]->get_type().get_endpoint(links[j]->get_endpoint_by_link(**i));
+ int p = get_turnout(tid2);
+ if(p>=0 && !(ep.paths&(1<<p)))
+ {
+ // The linked track is a turnout and has a path which is incompatible with this endpoint
+ mask &= ~endpoints[j].paths;
+ continue;
+ }
+ }
+ mask &= endpoints[j].paths;
+ }
+
+ if(mask && !(mask&(mask-1)))
+ {
+ // Exactly one possible choice, set the path accordingly
+ unsigned path = 0;
+ for(; (mask && !(mask&1)); mask>>=1, ++path) ;
+ turnouts[tid] = path;
+ }
+ else if(!turnouts.count(tid))
+ // More than one possible choice, and no existing entry - set as undecided
+ turnouts[tid] = -1;
+ }
+
+ // Remove any turnouts that do not exist in the route
+ for(map<unsigned, int>::iterator i=turnouts.begin(); i!=turnouts.end();)
+ {
+ if(!found.count(i->first))
+ turnouts.erase(i++);
+ else
+ ++i;
+ }
+}
+
+int Route::get_turnout(unsigned id) const
+{
+ map<unsigned, int>::const_iterator i = turnouts.find(id);
+ if(i!=turnouts.end())
+ return i->second;
+ return -1;
+}
+
+unsigned Route::get_path(Track &trk) const
+{
+ if(unsigned tid = trk.get_turnout_id())
+ {
+ map<unsigned, int>::const_iterator i = turnouts.find(tid);
+ if(i!=turnouts.end())
+ return i->second;
+ }
+ return trk.get_active_path();
+}
+
+void Route::add_track(Track &trk)
+{
+ if(tracks.count(&trk))
+ return;
+
+ if(!tracks.empty())
+ {
+ unsigned valid = check_validity(trk);
+ if(!(valid&1))
+ throw Exception("Not linked to existing tracks");
+ else if(!(valid&2))
+ throw Exception("Branching routes not allowed");
+ else if(!(valid&4))
+ throw Exception("Route must be smooth");
+ }
+
+ tracks.insert(&trk);
+ update_turnouts();
+}
+
+void Route::add_tracks(const set<Track *> &trks)
+{
+ set<Track *> pending;
+ for(set<Track *>::const_iterator i=trks.begin(); i!=trks.end(); ++i)
+ if(!tracks.count(*i))
+ pending.insert(*i);
+
+ while(!pending.empty())
+ {
+ bool found = false;
+ for(set<Track *>::const_iterator i=pending.begin(); i!=pending.end(); ++i)
+ if(tracks.empty() || check_validity(**i)==7)
+ {
+ tracks.insert(*i);
+ pending.erase(*i);
+ found = true;
+ break;
+ }
+
+ if(!found)
+ throw Exception("Could not add all tracks to route");
+ }
+
+ update_turnouts();
+}
+
+void Route::add_track_chain(Track &start, unsigned ep, const TurnoutMap &trnts)
+{
+ TrackIter iter(&start, ep);
+ while(iter)
+ {
+ if(iter->get_type().is_dead_end())
+ break;
+
+ if(has_track(*iter))
+ break;
+
+ int path = 0;
+ if(iter->get_turnout_id())
+ {
+ TurnoutMap::const_iterator i = trnts.find(iter->get_turnout_id());
+ if(i==trnts.end())
+ break;
+
+ path = i->second;
+ }
+
+ add_track(*iter);
+
+ iter = iter.next(path);
+ }
+}
+
+bool Route::has_track(Track &t) const
+{
+ return tracks.count(&t);
+}
+
+void Route::save(list<DataFile::Statement> &st) const
+{
+ st.push_back((DataFile::Statement("name"), name));
+ for(map<unsigned, int>::const_iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
+ st.push_back((DataFile::Statement("turnout"), i->first, i->second));
+}
+
+unsigned Route::check_validity(Track &trk) const
+{
+ unsigned result = 4;
+ const vector<Track *> &links = trk.get_links();
+ for(vector<Track *>::const_iterator i=links.begin(); i!=links.end(); ++i)
+ {
+ if(!*i)
+ continue;
+ if(!tracks.count(*i))
+ continue;
+
+ // Linked to an existing track - good
+ result |= 1;
+
+ if(unsigned tid = (*i)->get_turnout_id())
+ {
+ const TrackType::Endpoint &ep = (*i)->get_type().get_endpoint((*i)->get_endpoint_by_link(trk));
+ int path = get_turnout(tid);
+ if(path>=0)
+ {
+ // Linking to a turnout with path set is only good if we're continuing that path
+ if(ep.paths&(1<<path))
+ result |= 2;
+ }
+ else
+ {
+ // Linked to a turnout with no path set - check other linked tracks
+ const vector<Track *> &tlinks = (*i)->get_links();
+ unsigned count = 0;
+ for(unsigned j=0; j<tlinks.size(); ++j)
+ if(tracks.count(tlinks[j]))
+ {
+ unsigned tid2 = tlinks[j]->get_turnout_id();
+ if(tid2)
+ {
+ const TrackType::Endpoint &ep2 = tlinks[j]->get_type().get_endpoint(tlinks[j]->get_endpoint_by_link(**i));
+ path = get_turnout(tid2);
+ // Ignore a linked turnout with some other path set
+ if(path>0 && !(ep2.paths&(1<<path)))
+ continue;
+ }
+
+ ++count;
+
+ const TrackType::Endpoint &ep2 = (*i)->get_type().get_endpoint(j);
+ if(!(ep.paths&ep2.paths))
+ // Impossible path through the turnout - not good
+ result &= 3;
+ }
+
+ // Only good if at most one other track is linked to the turnout
+ if(count<=1)
+ result |= 2;
+ }
+ }
+ else
+ // Linked to something linear - good
+ result |= 2;
+ }
+
+ return result;
+}
+
+void Route::track_removed(Track &t)
+{
+ tracks.erase(&t);
+}
+
+Route *Route::find(const TrackIter &from, Track &to)
+{
+ return create_route(from, TrackMatch(to));
+}
+
+Route *Route::find(const TrackIter &from, const Route &to)
+{
+ return create_route(from, TrackInSet(to.get_tracks()));
+}
+
+Route *Route::find(const TrackIter &from, const set<Track *> &to)
+{
+ return create_route(from, TrackInSet(to));
+}
+
+
+Route::Loader::Loader(Route &r):
+ DataFile::BasicLoader<Route>(r)
+{
+ add("name", &Route::name);
+ add("turnout", &Loader::turnout);
+}
+
+void Route::Loader::finish()
+{
+ const set<Track *> <racks = obj.layout.get_tracks();
+ for(set<Track *>::const_iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
+ {
+ unsigned tid = (*i)->get_turnout_id();
+ if(!tid)
+ continue;
+
+ TurnoutMap::iterator j = turnouts.find(tid);
+ if(j==turnouts.end())
+ continue;
+
+ unsigned path_mask = 1<<j->second;
+ const vector<TrackType::Endpoint> &eps = (*i)->get_type().get_endpoints();
+ for(unsigned k=0; k<eps.size(); ++k)
+ if(eps[k].paths&path_mask)
+ {
+ Track *link = (*i)->get_link(k);
+ if(!obj.tracks.count(link))
+ obj.add_track_chain(*link, link->get_endpoint_by_link(**i), turnouts);
+ if(!obj.tracks.count(*i))
+ obj.add_track_chain(**i, k, turnouts);
+ break;
+ }
+
+ break;
+ }
+}
+
+void Route::Loader::turnout(unsigned id, unsigned path)
+{
+ turnouts[id] = path;
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2007-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_ROUTE_H_
+#define LIBR2C2_ROUTE_H_
+
+#include <map>
+#include <set>
+#include <string>
+#include <sigc++/trackable.h>
+#include <msp/datafile/loader.h>
+
+namespace R2C2 {
+
+class Layout;
+class Track;
+class TrackIter;
+
+class Route: public sigc::trackable
+{
+public:
+ typedef std::map<unsigned, int> TurnoutMap;
+
+ class Loader: public Msp::DataFile::BasicLoader<Route>
+ {
+ private:
+ TurnoutMap turnouts;
+
+ public:
+ Loader(Route &);
+ private:
+ virtual void finish();
+ void turnout(unsigned, unsigned);
+ };
+
+ sigc::signal<void, const std::string &> signal_name_changed;
+
+private:
+ Layout &layout;
+ std::string name;
+ bool temporary;
+ std::set<Track *> tracks;
+ TurnoutMap turnouts;
+
+public:
+ Route(Layout &);
+ ~Route();
+
+ void set_name(const std::string &);
+ const std::string &get_name() const { return name; }
+ void set_temporary(bool);
+ bool is_temporary() const { return temporary; }
+ void set_turnout(unsigned, unsigned);
+ void update_turnouts();
+ int get_turnout(unsigned) const;
+ unsigned get_path(Track &) const;
+ const std::map<unsigned, int> &get_turnouts() const { return turnouts; }
+ void add_track(Track &);
+ void add_tracks(const std::set<Track *> &);
+ void add_track_chain(Track &, unsigned, const TurnoutMap &);
+ const std::set<Track *> &get_tracks() const { return tracks; }
+ bool has_track(Track &) const;
+ void save(std::list<Msp::DataFile::Statement> &) const;
+private:
+ unsigned check_validity(Track &) const;
+ void track_removed(Track &);
+
+public:
+ static Route *find(const TrackIter &, Track &);
+ static Route *find(const TrackIter &, const Route &);
+ static Route *find(const TrackIter &, const std::set<Track *> &);
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/core/except.h>
+#include <msp/time/units.h>
+#include "simplecontroller.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+SimpleController::SimpleController():
+ target_speed(Control::continuous("speed", 0, 1000)),
+ reverse(Control::binary("reverse")),
+ accel(0.07),
+ speed(0)
+{
+ target_speed.set(0);
+}
+
+void SimpleController::set_control(const string &name, float v)
+{
+ if(name=="speed")
+ {
+ target_speed.set(v);
+ signal_control_changed.emit(target_speed);
+ }
+ else if(name=="reverse")
+ {
+ if(target_speed.value || speed)
+ throw InvalidState("Must be stopped to change reverse");
+ reverse.set(v);
+ signal_control_changed.emit(reverse);
+ }
+}
+
+const Controller::Control &SimpleController::get_control(const string &name) const
+{
+ if(name=="speed")
+ return target_speed;
+ else if(name=="reverse")
+ return reverse;
+ else
+ throw KeyError("Unknown control", name);
+}
+
+float SimpleController::get_braking_distance() const
+{
+ return speed*speed/(2*accel);
+}
+
+void SimpleController::tick(const Time::TimeDelta &dt)
+{
+ float secs = dt/Time::sec;
+ if(speed<target_speed.value)
+ {
+ speed += secs*accel;
+ if(speed>target_speed.value)
+ speed = target_speed.value;
+ }
+ else if(speed>target_speed.value)
+ {
+ speed -= secs*accel;
+ if(speed<target_speed.value)
+ speed = target_speed.value;
+ }
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_SIMPLECONTROLLER_H_
+#define LIBR2C2_SIMPLECONTROLLER_H_
+
+#include <string>
+#include "controller.h"
+
+namespace R2C2 {
+
+class SimpleController: public Controller
+{
+private:
+ Control target_speed;
+ Control reverse;
+ float accel;
+ float speed;
+
+public:
+ SimpleController();
+
+ virtual void set_control(const std::string &, float);
+ virtual const Control &get_control(const std::string &) const;
+
+ virtual float get_speed() const { return speed; }
+ virtual bool get_reverse() const { return reverse.value; }
+ virtual float get_braking_distance() const;
+
+ virtual void tick(const Msp::Time::TimeDelta &);
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/strings/formatter.h>
+#include <msp/time/units.h>
+#include "block.h"
+#include "catalogue.h"
+#include "driver.h"
+#include "layout.h"
+#include "timetable.h"
+#include "train.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+Timetable::Timetable(Train &t):
+ train(t),
+ enabled(false),
+ current_row(0),
+ executing(true),
+ pending_block(0),
+ pending_train(0)
+{
+ train.signal_arrived.connect(sigc::mem_fun(this, &Timetable::train_arrived));
+ train.get_layout().get_driver().signal_sensor.connect(sigc::mem_fun(this, &Timetable::sensor_event));
+}
+
+void Timetable::set_enabled(bool e)
+{
+ enabled = e;
+}
+
+void Timetable::reset()
+{
+ current_row = 0;
+ wait_timeout = Time::TimeStamp();
+ pending_block = 0;
+ executing = true;
+}
+
+void Timetable::clear()
+{
+ rows.clear();
+ reset();
+}
+
+void Timetable::append(const Row &row)
+{
+ rows.push_back(row);
+}
+
+void Timetable::insert(unsigned i, const Row &row)
+{
+ if(i>rows.size())
+ throw InvalidParameterValue("Insert position out of range");
+
+ rows.insert(rows.begin()+i, row);
+ if(i<=current_row)
+ ++current_row;
+}
+
+const Timetable::Row &Timetable::get_row(unsigned i) const
+{
+ if(i>=rows.size())
+ throw InvalidParameterValue("Row index out of range");
+ return rows[i];
+}
+
+void Timetable::tick(const Time::TimeStamp &t)
+{
+ if(rows.empty() || !enabled)
+ return;
+
+ if(wait_timeout && t>=wait_timeout)
+ {
+ wait_timeout = Time::TimeStamp();
+ current_row = (current_row+1)%rows.size();
+ executing = true;
+ }
+
+ if(executing)
+ {
+ Row &row = rows[current_row];
+ switch(row.type)
+ {
+ case GOTO:
+ if(!train.go_to(**parse_location(row.get_param<string>(0)).get_tracks().begin()))
+ set_enabled(false);
+ break;
+ case TRAVEL:
+ pending_block = &parse_location(row.get_param<string>(0));
+ pending_train = &train;
+ executing = false;
+ break;
+ case WAIT_TIME:
+ wait_timeout = t+row.get_param<unsigned>(0)*Time::sec;
+ executing = false;
+ break;
+ case WAIT_TRAIN:
+ pending_train = &train.get_layout().get_train(row.get_param<unsigned>(0));
+ pending_block = &parse_location(row.get_param<string>(1));
+ executing = false;
+ break;
+ case ARRIVE:
+ executing = false;
+ break;
+ case SPEED:
+ train.set_control("speed", row.get_param<unsigned>(0)/3.6*train.get_layout().get_catalogue().get_scale());
+ break;
+ case REVERSE:
+ train.set_control("reverse", !train.get_control("reverse"));
+ break;
+ case ROUTE:
+ if(!train.set_route(&train.get_layout().get_route(row.get_param<string>(0))))
+ set_enabled(false);
+ break;
+ }
+
+ if(executing)
+ current_row = (current_row+1)%rows.size();
+ }
+}
+
+void Timetable::save(list<DataFile::Statement> &st) const
+{
+ for(vector<Row>::const_iterator i=rows.begin(); i!=rows.end(); ++i)
+ st.push_back(i->save());
+}
+
+Block &Timetable::parse_location(const string &loc)
+{
+ if(!loc.compare(0, 7, "sensor "))
+ return train.get_layout().get_block(lexical_cast<unsigned>(loc.substr(7))|0x1000);
+ throw Exception("Named blocks are not supported yet");
+}
+
+void Timetable::sensor_event(unsigned addr, bool state)
+{
+ if(pending_block && pending_block->get_train()==pending_train && addr==pending_block->get_sensor_id() && state)
+ {
+ pending_block = 0;
+ current_row = (current_row+1)%rows.size();
+ executing = true;
+ }
+}
+
+void Timetable::train_arrived()
+{
+ Row &row = rows[current_row];
+ if(row.type==ARRIVE)
+ {
+ current_row = (current_row+1)%rows.size();
+ executing = true;
+ }
+}
+
+
+Timetable::Row::Row(RowType t):
+ type(t)
+{ }
+
+template<typename T>
+Timetable::Row::Row(RowType t, const T &p):
+ type(t)
+{
+ params.push_back(p);
+}
+
+template<typename T>
+const T &Timetable::Row::get_param(unsigned i) const
+{
+ if(i>=params.size())
+ throw InvalidParameterValue("Parameter index out of range");
+ return params[i].value<T>();
+}
+
+string Timetable::Row::str() const
+{
+ switch(type)
+ {
+ case GOTO:
+ return "set route to "+get_param<string>(0);
+ case TRAVEL:
+ return "travel to "+get_param<string>(0);
+ case WAIT_TIME:
+ return format("wait for %d seconds", get_param<unsigned>(0));
+ case WAIT_TRAIN:
+ return format("wait for train %d at %s", get_param<unsigned>(0), get_param<string>(1));
+ case ARRIVE:
+ return "travel until arrival";
+ case SPEED:
+ return format("set speed %d km/h", get_param<unsigned>(0));
+ case REVERSE:
+ return "reverse";
+ case ROUTE:
+ return "set route "+get_param<string>(0);
+ default:
+ return "invalid row";
+ }
+}
+
+DataFile::Statement Timetable::Row::save() const
+{
+ switch(type)
+ {
+ case GOTO:
+ return DataFile::Statement("goto"), get_param<string>(0);
+ case TRAVEL:
+ return DataFile::Statement("travel"), get_param<string>(0);
+ case WAIT_TIME:
+ return DataFile::Statement("wait"), get_param<unsigned>(0);
+ case WAIT_TRAIN:
+ return DataFile::Statement("wait_train"), get_param<unsigned>(0), get_param<string>(1);
+ case ARRIVE:
+ return DataFile::Statement("arrive");
+ case SPEED:
+ return DataFile::Statement("speed"), get_param<unsigned>(0);
+ case REVERSE:
+ return DataFile::Statement("reverse");
+ case ROUTE:
+ return DataFile::Statement("route"), get_param<string>(0);
+ default:
+ return DataFile::Statement();
+ }
+}
+
+Timetable::Row Timetable::Row::parse(const string &s)
+{
+ if(!s.compare(0, 7, "travel "))
+ {
+ if(!s.compare(7, 3, "to "))
+ return Row(TRAVEL, s.substr(10));
+ else if(!s.compare(7, string::npos, "until arrival"))
+ return Row(ARRIVE);
+ }
+ else if(!s.compare(0, 9, "wait for "))
+ {
+ if(isdigit(s[9]))
+ {
+ unsigned nondigit = 10;
+ while(nondigit<s.size() && isdigit(s[nondigit]))
+ ++nondigit;
+ return Row(WAIT_TIME, lexical_cast<unsigned>(s.substr(9, nondigit-9)));
+ }
+ else if(!s.compare(9, 6, "train "))
+ {
+ string::size_type at = s.find(" at ", 15);
+ if(at!=string::npos)
+ {
+ Row row(WAIT_TRAIN, lexical_cast<unsigned>(s.substr(15, at-15)));
+ row.params.push_back(s.substr(at+4));
+ return row;
+ }
+ }
+ }
+ else if(!s.compare(0, 10, "set speed "))
+ {
+ unsigned nondigit = 11;
+ while(nondigit<s.size() && (isdigit(s[nondigit]) || s[nondigit]=='-'))
+ ++nondigit;
+ return Row(SPEED, lexical_cast<unsigned>(s.substr(10, nondigit-10)));
+ }
+ else if(s=="reverse")
+ return Row(REVERSE);
+ else if(!s.compare(0, 10, "set route "))
+ {
+ if(!s.compare(10, 3, "to "))
+ return Row(GOTO, s.substr(13));
+ return Row(ROUTE, s.substr(10));
+ }
+
+ throw InvalidParameterValue("Invalid row");
+}
+
+
+Timetable::Loader::Loader(Timetable &tt):
+ DataFile::ObjectLoader<Timetable>(tt)
+{
+ add("arrive", &Loader::arrive);
+ add("goto", &Loader::go_to);
+ add("route", &Loader::route);
+ add("speed", &Loader::speed);
+ add("reverse", &Loader::reverse);
+ add("travel", &Loader::travel);
+ add("wait", &Loader::wait);
+ add("wait_train", &Loader::wait_train);
+}
+
+void Timetable::Loader::arrive()
+{
+ obj.rows.push_back(Row(ARRIVE));
+}
+
+void Timetable::Loader::go_to(const string &t)
+{
+ obj.rows.push_back(Row(GOTO, t));
+}
+
+void Timetable::Loader::route(const string &r)
+{
+ obj.rows.push_back(Row(ROUTE, r));
+}
+
+void Timetable::Loader::reverse()
+{
+ obj.rows.push_back(Row(REVERSE));
+}
+
+void Timetable::Loader::speed(unsigned s)
+{
+ obj.rows.push_back(Row(SPEED, s));
+}
+
+void Timetable::Loader::travel(const string &t)
+{
+ obj.rows.push_back(Row(TRAVEL, t));
+}
+
+void Timetable::Loader::wait(unsigned t)
+{
+ obj.rows.push_back(Row(WAIT_TIME, t));
+}
+
+void Timetable::Loader::wait_train(unsigned t, const string &b)
+{
+ Row row(WAIT_TRAIN, t);
+ row.params.push_back(b);
+ obj.rows.push_back(row);
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_TIMETABLE_H_
+#define LIBR2C2_TIMETABLE_H_
+
+#include <string>
+#include <vector>
+#include <sigc++/trackable.h>
+#include <msp/datafile/objectloader.h>
+#include <msp/time/timestamp.h>
+
+namespace R2C2 {
+
+class Block;
+class Train;
+
+class Timetable: public sigc::trackable
+{
+public:
+ class Loader: public Msp::DataFile::ObjectLoader<Timetable>
+ {
+ public:
+ Loader(Timetable &);
+ private:
+ void arrive();
+ void go_to(const std::string &);
+ void route(const std::string &);
+ void reverse();
+ void speed(unsigned);
+ void travel(const std::string &);
+ void wait(unsigned);
+ void wait_train(unsigned, const std::string &);
+ };
+
+ enum RowType
+ {
+ GOTO,
+ TRAVEL,
+ WAIT_TIME,
+ WAIT_TRAIN,
+ ARRIVE,
+ SPEED,
+ REVERSE,
+ ROUTE
+ };
+
+ struct Row
+ {
+ RowType type;
+ std::vector<Msp::Variant> params;
+
+ Row(RowType);
+
+ template<typename T>
+ Row(RowType, const T &);
+
+ template<typename T>
+ const T &get_param(unsigned) const;
+
+ std::string str() const;
+
+ Msp::DataFile::Statement save() const;
+
+ static Row parse(const std::string &);
+ };
+
+private:
+ Train &train;
+ bool enabled;
+ std::vector<Row> rows;
+ unsigned current_row;
+ bool executing;
+ Block *pending_block;
+ Train *pending_train;
+ Msp::Time::TimeStamp wait_timeout;
+
+public:
+ Timetable(Train &);
+
+ void set_enabled(bool);
+ bool is_enabled() const { return enabled; }
+ void reset();
+
+ void clear();
+ void append(const Row &);
+ void insert(unsigned, const Row &);
+ unsigned get_n_rows() const { return rows.size(); }
+ const Row &get_row(unsigned) const;
+
+ void tick(const Msp::Time::TimeStamp &);
+ void save(std::list<Msp::DataFile::Statement> &) const;
+private:
+ Block &parse_location(const std::string &);
+ void sensor_event(unsigned, bool);
+ void train_arrived();
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <cmath>
+#include "block.h"
+#include "catalogue.h"
+#include "driver.h"
+#include "layout.h"
+#include "track.h"
+#include "tracktype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+Track::Track(Layout &l, const TrackType &t):
+ layout(l),
+ type(t),
+ block(0),
+ rot(0),
+ slope(0),
+ flex(false),
+ turnout_id(type.is_turnout() ? layout.allocate_turnout_id(type.is_double_address()) : 0),
+ sensor_id(0),
+ links(type.get_endpoints().size()),
+ active_path(0)
+{
+ layout.add_track(*this);
+
+ if(layout.has_driver())
+ layout.get_driver().signal_turnout.connect(sigc::mem_fun(this, &Track::turnout_event));
+
+ for(unsigned paths = type.get_paths(); !(paths&1); ++active_path, paths>>=1) ;
+}
+
+Track::~Track()
+{
+ break_links();
+ layout.remove_track(*this);
+}
+
+void Track::set_block(Block *b)
+{
+ if(b && !b->has_track(*this))
+ throw InvalidParameterValue("Track is not in the Block");
+ if(!b && block && block->has_track(*this))
+ throw InvalidState("Track is still in a Block");
+
+ block = b;
+}
+
+Block &Track::get_block() const
+{
+ if(!block)
+ throw InvalidState("No Block");
+
+ return *block;
+}
+
+void Track::set_position(const Point &p)
+{
+ pos = p;
+}
+
+void Track::set_rotation(float r)
+{
+ rot = r;
+ while(rot<0)
+ rot += M_PI*2;
+ while(rot>M_PI*2)
+ rot -= M_PI*2;
+}
+
+void Track::set_slope(float s)
+{
+ if(links.size()!=2)
+ return;
+
+ slope = s;
+}
+
+void Track::set_flex(bool f)
+{
+ flex = f;
+}
+
+void Track::check_slope()
+{
+ if(links.size()!=2)
+ return;
+
+ if(links[0] && links[1])
+ {
+ Point epp0 = links[0]->get_endpoint_position(links[0]->get_endpoint_by_link(*this));
+ Point epp1 = links[1]->get_endpoint_position(links[1]->get_endpoint_by_link(*this));
+ pos.z = epp0.z;
+ slope = epp1.z-pos.z;
+ }
+ else
+ {
+ slope = 0;
+ if(links[0])
+ {
+ Point epp = links[0]->get_endpoint_position(links[0]->get_endpoint_by_link(*this));
+ pos.z = epp.z;
+ }
+ else if(links[1])
+ {
+ Point epp = links[1]->get_endpoint_position(links[1]->get_endpoint_by_link(*this));
+ pos.z = epp.z;
+ }
+ }
+}
+
+void Track::set_turnout_id(unsigned i)
+{
+ if(!type.is_turnout())
+ throw InvalidState("Not a turnout");
+
+ turnout_id = i;
+ layout.create_blocks(*this);
+ layout.update_routes();
+ if(layout.has_driver() && turnout_id)
+ {
+ layout.get_driver().add_turnout(turnout_id);
+ if(type.is_double_address())
+ layout.get_driver().add_turnout(turnout_id+1);
+ }
+}
+
+void Track::set_sensor_id(unsigned i)
+{
+ if(type.is_turnout())
+ throw InvalidState("Can't set sensor on a turnout");
+
+ sensor_id = i;
+ layout.create_blocks(*this);
+ if(layout.has_driver() && sensor_id)
+ layout.get_driver().add_sensor(sensor_id);
+}
+
+void Track::set_active_path(unsigned p)
+{
+ if(!turnout_id)
+ throw InvalidState("Not a turnout");
+ if(!(type.get_paths()&(1<<p)))
+ throw InvalidParameterValue("Invalid path");
+
+ layout.get_driver().set_turnout(turnout_id, p&1);
+ if(type.is_double_address())
+ layout.get_driver().set_turnout(turnout_id+1, p&2);
+ else if(type.get_n_paths()>2)
+ active_path = (active_path&1) | (p&2);
+}
+
+int Track::get_endpoint_by_link(Track &other) const
+{
+ for(unsigned i=0; i<links.size(); ++i)
+ if(links[i]==&other)
+ return i;
+
+ return -1;
+}
+
+Point Track::get_endpoint_position(unsigned epi) const
+{
+ const vector<TrackType::Endpoint> &eps = type.get_endpoints();
+ if(epi>=eps.size())
+ throw InvalidParameterValue("TrackType::Endpoint index out of range");
+
+ const TrackType::Endpoint &ep = eps[epi];
+
+ float c = cos(rot);
+ float s = sin(rot);
+
+ Point p(pos.x+c*ep.pos.x-s*ep.pos.y, pos.y+s*ep.pos.x+c*ep.pos.y, pos.z);
+ if(eps.size()==2 && epi==1)
+ p.z += slope;
+ return p;
+}
+
+float Track::get_endpoint_direction(unsigned epi) const
+{
+ const vector<TrackType::Endpoint> &eps = type.get_endpoints();
+ if(epi>=eps.size())
+ throw InvalidParameterValue("TrackType::Endpoint index out of range");
+
+ const TrackType::Endpoint &ep = eps[epi];
+
+ return rot+ep.dir;
+}
+
+bool Track::snap_to(Track &other, bool link, float limit)
+{
+ if(!limit || link)
+ {
+ limit = layout.get_catalogue().get_gauge();
+ if(link && !flex && !other.get_flex())
+ limit /= 10;
+ }
+ limit *= limit;
+
+ const vector<TrackType::Endpoint> &eps = type.get_endpoints();
+ const vector<TrackType::Endpoint> &other_eps = other.get_type().get_endpoints();
+
+ for(unsigned i=0; i<eps.size(); ++i)
+ {
+ Point epp = get_endpoint_position(i);
+
+ for(unsigned j=0; j<other_eps.size(); ++j)
+ {
+ if(other.get_link(j))
+ continue;
+
+ Point epp2 = other.get_endpoint_position(j);
+ float dx = epp2.x-epp.x;
+ float dy = epp2.y-epp.y;
+ float dz = epp2.z-epp.z;
+ if(dx*dx+dy*dy<limit && dz*dz<limit)
+ {
+ if(!link || (!flex && !other.get_flex()))
+ {
+ set_rotation(other.rot+other_eps[j].dir-eps[i].dir+M_PI);
+ Point p(epp2.x-(eps[i].pos.x*cos(rot)-eps[i].pos.y*sin(rot)),
+ epp2.y-(eps[i].pos.y*cos(rot)+eps[i].pos.x*sin(rot)),
+ epp2.z);
+ if(eps.size()==2 && i==1)
+ p.z -= slope;
+ set_position(p);
+ }
+
+ if(link)
+ {
+ if(links[i])
+ break_link(*links[i]);
+ links[i] = &other;
+ other.links[j] = this;
+ layout.create_blocks(*this);
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool Track::snap(Point &pt, float &d) const
+{
+ const vector<TrackType::Endpoint> &eps = type.get_endpoints();
+
+ for(unsigned i=0; i<eps.size(); ++i)
+ {
+ Point epp = get_endpoint_position(i);
+ float dx = pt.x-epp.x;
+ float dy = pt.y-epp.y;
+ if(dx*dx+dy*dy<1e-4)
+ {
+ pt = epp;
+ d = rot+eps[i].dir;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Track::break_link(Track &trk)
+{
+ for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
+ if(*i==&trk)
+ {
+ *i = 0;
+ trk.break_link(*this);
+ // XXX Creates the blocks twice
+ layout.create_blocks(*this);
+ return;
+ }
+}
+
+void Track::break_links()
+{
+ for(vector<Track *>::iterator i=links.begin(); i!=links.end(); ++i)
+ if(Track *trk=*i)
+ {
+ *i = 0;
+ trk->break_link(*this);
+ }
+}
+
+Track *Track::get_link(unsigned i) const
+{
+ if(i>links.size())
+ throw InvalidParameterValue("Link index out of range");
+
+ return links[i];
+}
+
+TrackPoint Track::get_point(unsigned epi, unsigned path, float d) const
+{
+ TrackPoint p = type.get_point(epi, path, d);
+ float c = cos(rot);
+ float s = sin(rot);
+
+ p.pos = Point(pos.x+c*p.pos.x-s*p.pos.y, pos.y+s*p.pos.x+c*p.pos.y, pos.z);
+ p.dir += rot;
+ if(type.get_endpoints().size()==2)
+ {
+ float len = type.get_path_length(path);
+ float grade = slope/len;
+ if(epi==0)
+ {
+ p.pos.z += grade*d;
+ p.grade = grade;
+ }
+ else
+ {
+ p.pos.z += slope-grade*d;
+ p.grade = -grade;
+ }
+ }
+
+ return p;
+}
+
+TrackPoint Track::get_point(unsigned epi, float d) const
+{
+ return get_point(epi, active_path, d);
+}
+
+void Track::save(list<DataFile::Statement> &st) const
+{
+ st.push_back((DataFile::Statement("position"), pos.x, pos.y, pos.z));
+ st.push_back((DataFile::Statement("rotation"), rot));
+ st.push_back((DataFile::Statement("slope"), slope));
+ if(turnout_id)
+ st.push_back((DataFile::Statement("turnout_id"), turnout_id));
+ if(sensor_id)
+ st.push_back((DataFile::Statement("sensor_id"), sensor_id));
+ if(flex)
+ st.push_back((DataFile::Statement("flex"), true));
+}
+
+void Track::turnout_event(unsigned addr, bool state)
+{
+ if(!turnout_id)
+ return;
+
+ if(addr==turnout_id)
+ active_path = (active_path&2) | (state ? 1 : 0);
+ else if(type.is_double_address() && addr==turnout_id+1)
+ active_path = (active_path&1) | (state ? 2 : 0);
+ else
+ return;
+
+ signal_path_changed.emit(active_path);
+}
+
+
+Track::Loader::Loader(Track &t):
+ DataFile::BasicLoader<Track>(t)
+{
+ add("position", &Loader::position);
+ add("rotation", &Track::rot);
+ add("slope", &Track::slope);
+ add("turnout_id", &Loader::turnout_id);
+ add("sensor_id", &Loader::sensor_id);
+ add("flex", &Track::flex);
+}
+
+void Track::Loader::position(float x, float y, float z)
+{
+ obj.pos = Point(x, y, z);
+}
+
+void Track::Loader::sensor_id(unsigned id)
+{
+ obj.set_sensor_id(id);
+}
+
+void Track::Loader::turnout_id(unsigned id)
+{
+ obj.set_turnout_id(id);
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_TRACK_H_
+#define LIBR2C2_TRACK_H_
+
+#include <list>
+#include <set>
+#include <sigc++/trackable.h>
+#include <msp/datafile/loader.h>
+#include "geometry.h"
+
+namespace R2C2 {
+
+class Block;
+class Layout;
+class TrackType;
+
+class Track: public sigc::trackable
+{
+public:
+ class Loader: public Msp::DataFile::BasicLoader<Track>
+ {
+ public:
+ Loader(Track &);
+ private:
+ void position(float, float, float);
+ void sensor_id(unsigned);
+ void turnout_id(unsigned);
+ };
+
+ sigc::signal<void, unsigned> signal_path_changed;
+
+private:
+ Layout &layout;
+ const TrackType &type;
+ Block *block;
+ Point pos;
+ float rot;
+ float slope;
+ bool flex;
+ unsigned turnout_id;
+ unsigned sensor_id;
+ std::vector<Track *> links;
+ unsigned active_path;
+
+ Track(const Track &);
+ Track &operator=(const Track &);
+public:
+ Track(Layout &, const TrackType &);
+ ~Track();
+
+ Layout &get_layout() const { return layout; }
+ const TrackType &get_type() const { return type; }
+
+ void set_block(Block *);
+ Block &get_block() const;
+ void set_position(const Point &);
+ void set_rotation(float);
+ void set_slope(float);
+ void set_flex(bool);
+ const Point &get_position() const { return pos; }
+ float get_rotation() const { return rot; }
+ float get_slope() const { return slope; }
+ bool get_flex() const { return flex; }
+ void check_slope();
+
+ void set_turnout_id(unsigned);
+ void set_sensor_id(unsigned);
+ unsigned get_turnout_id() const { return turnout_id; }
+ unsigned get_sensor_id() const { return sensor_id; }
+ void set_active_path(unsigned);
+ unsigned get_active_path() const { return active_path; }
+
+ int get_endpoint_by_link(Track &) const;
+ Point get_endpoint_position(unsigned) const;
+ float get_endpoint_direction(unsigned) const;
+ bool snap_to(Track &, bool, float = 0);
+ bool snap(Point &, float &) const;
+ void break_link(Track &);
+ void break_links();
+ const std::vector<Track *> &get_links() const { return links; }
+ Track *get_link(unsigned) const;
+ TrackPoint get_point(unsigned, unsigned, float) const;
+ TrackPoint get_point(unsigned, float) const;
+
+ void save(std::list<Msp::DataFile::Statement> &) const;
+private:
+ void turnout_event(unsigned, bool);
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <algorithm>
+#include <msp/core/except.h>
+#include "track.h"
+#include "trackiter.h"
+#include "tracktype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+TrackIter::TrackIter():
+ _track(0),
+ _entry(0)
+{ }
+
+TrackIter::TrackIter(Track *t, unsigned e):
+ _track(t),
+ _entry(t ? e : 0)
+{
+ if(_track && _entry>_track->get_type().get_endpoints().size())
+ throw InvalidParameterValue("Endpoint index not valid for track");
+}
+
+const TrackType::Endpoint &TrackIter::endpoint() const
+{
+ if(!_track)
+ throw InvalidState("TrackIter is null");
+
+ return _track->get_type().get_endpoint(_entry);
+}
+
+int TrackIter::get_exit(unsigned path) const
+{
+ const vector<TrackType::Endpoint> &eps = _track->get_type().get_endpoints();
+
+ // Find an endpoint that's connected to the entry and has the requested path
+ for(unsigned i=0; i<eps.size(); ++i)
+ if(i!=_entry && (eps[i].paths&(1<<path)) && (eps[i].paths&eps[_entry].paths))
+ return i;
+
+ return -1;
+}
+
+TrackIter TrackIter::next() const
+{
+ if(!_track)
+ return TrackIter();
+
+ return next(_track->get_active_path());
+}
+
+TrackIter TrackIter::next(unsigned path) const
+{
+ if(!_track)
+ return TrackIter();
+
+ int exit = get_exit(path);
+ if(exit<0)
+ return TrackIter();
+
+ TrackIter result;
+ result._track = _track->get_link(exit);
+ result._entry = (result._track ? result._track->get_endpoint_by_link(*_track) : 0);
+
+ return result;
+}
+
+TrackIter TrackIter::reverse() const
+{
+ if(!_track)
+ return TrackIter();
+
+ return reverse(_track->get_active_path());
+}
+
+TrackIter TrackIter::reverse(unsigned path) const
+{
+ if(!_track)
+ return TrackIter();
+
+ int exit = get_exit(path);
+ if(exit<0)
+ return TrackIter();
+
+ return TrackIter(_track, exit);
+}
+
+TrackIter TrackIter::flip() const
+{
+ if(!_track)
+ return TrackIter();
+
+ TrackIter result;
+ result._track = _track->get_link(_entry);
+ result._entry = (result._track ? result._track->get_endpoint_by_link(*_track) : 0);
+
+ return result;
+}
+
+Track &TrackIter::operator*() const
+{
+ if(!_track)
+ throw InvalidState("TrackIter is null");
+
+ return *_track;
+}
+
+bool TrackIter::operator==(const TrackIter &other) const
+{
+ return _track==other._track && _entry==other._entry;
+}
+
+
+TrackLoopIter::TrackLoopIter():
+ _looped(false)
+{ }
+
+TrackLoopIter::TrackLoopIter(Track *t, unsigned e):
+ TrackIter(t, e),
+ _visited(new TrackList()),
+ _last(_visited->insert(_visited->end(), track())),
+ _looped(false)
+{ }
+
+TrackLoopIter::TrackLoopIter(const TrackIter &i):
+ TrackIter(i),
+ _visited(new TrackList()),
+ _last(_visited->insert(_visited->end(), track())),
+ _looped(false)
+{ }
+
+TrackLoopIter::TrackLoopIter(const TrackIter &i, RefPtr<TrackList> v, const TrackList::iterator &l):
+ TrackIter(i),
+ _looped(false)
+{
+ if(track())
+ {
+ _visited = v;
+ _last = l;
+ _looped = (_visited && find(_visited->begin(), _last, track())!=_last);
+
+ ++_last;
+ if(_last!=_visited->end())
+ {
+ _visited = new TrackList(_visited->begin(), _last);
+ _last = _visited->end();
+ }
+ _visited->push_back(track());
+ --_last;
+ }
+}
+
+TrackLoopIter TrackLoopIter::next() const
+{
+ return TrackLoopIter(TrackIter::next(), _visited, _last);
+}
+
+TrackLoopIter TrackLoopIter::next(unsigned path) const
+{
+ return TrackLoopIter(TrackIter::next(path), _visited, _last);
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_TRACKITER_H_
+#define LIBR2C2_TRACKITER_H_
+
+#include <set>
+#include <msp/core/refptr.h>
+#include "tracktype.h"
+
+namespace R2C2 {
+
+class Track;
+
+/**
+An iterator for traversing tracks.
+*/
+class TrackIter
+{
+private:
+ Track *_track;
+ unsigned _entry;
+
+public:
+ TrackIter();
+ TrackIter(Track *, unsigned);
+
+ Track *track() const { return _track; }
+ unsigned entry() const { return _entry; }
+ const TrackType::Endpoint &endpoint() const;
+
+private:
+ int get_exit(unsigned) const;
+public:
+ TrackIter next() const;
+ TrackIter next(unsigned) const;
+ TrackIter reverse() const;
+ TrackIter reverse(unsigned) const;
+ TrackIter flip() const;
+
+ Track &operator*() const;
+ Track *operator->() const { return _track; }
+ bool operator==(const TrackIter &) const;
+ bool operator!=(const TrackIter &other) const { return !(*this==other); }
+ operator bool() const { return _track!=0; }
+};
+
+
+/**
+A track iterator that detects looping.
+
+A list of visited tracks is maintained internally to the iterator. This list
+is shared between iterators as long as next() is only called once per iterator.
+Subsequent calls to next() cause the head of the list to be copied.
+*/
+class TrackLoopIter: public TrackIter
+{
+private:
+ typedef std::list<Track *> TrackList;
+
+ Msp::RefPtr<TrackList> _visited;
+ TrackList::iterator _last;
+ bool _looped;
+
+public:
+ TrackLoopIter();
+ TrackLoopIter(Track *, unsigned);
+ TrackLoopIter(const TrackIter &);
+private:
+ TrackLoopIter(const TrackIter &, Msp::RefPtr<TrackList>, const TrackList::iterator &);
+
+public:
+ bool looped() const { return _looped; }
+
+ TrackLoopIter next() const;
+ TrackLoopIter next(unsigned) const;
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <cmath>
+#include "trackpart.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+TrackPart::TrackPart():
+ dir(0),
+ length(0),
+ radius(0),
+ path(0),
+ dead_end(false)
+{
+ links[0] = 0;
+ links[1] = 0;
+}
+
+float TrackPart::get_length() const
+{
+ if(radius)
+ return abs(radius)*length;
+ else
+ return length;
+}
+
+TrackPoint TrackPart::get_point(float d) const
+{
+ TrackPoint result;
+
+ if(radius)
+ {
+ float a = d/radius;
+ float c = cos(a);
+ float s = sin(a);
+ float rx = radius*sin(dir);
+ float ry = -radius*cos(dir);
+ result.pos = Point(pos.x+c*rx-s*ry-rx, pos.y+c*ry+s*rx-ry);
+ result.dir = dir+a;
+ }
+ else
+ {
+ result.pos = Point(pos.x+cos(dir)*d, pos.y+sin(dir)*d);
+ result.dir = dir;
+ }
+
+ return result;
+}
+
+void TrackPart::check_link(TrackPart &other)
+{
+ unsigned n_eps = (dead_end ? 1 : 2);
+ unsigned n_other_eps = (other.is_dead_end() ? 1 : 2);
+ for(unsigned i=0; i<n_eps; ++i)
+ {
+ TrackPoint p1 = get_point(i ? get_length() : 0);
+ for(unsigned j=0; j<n_other_eps; ++j)
+ {
+ TrackPoint p2 = other.get_point(j ? other.get_length() : 0);
+
+ float dx = p2.pos.x-p1.pos.x;
+ float dy = p2.pos.y-p1.pos.y;
+
+ float da = p2.dir-p1.dir+M_PI*((i+j+1)%2);
+ while(da>M_PI)
+ da -= M_PI*2;
+ while(da<-M_PI)
+ da += M_PI*2;
+
+ if(dx*dx+dy*dy<1e-6 && da>=-0.01 && da<=0.01)
+ {
+ links[i] = &other;
+ other.links[j] = this;
+ return;
+ }
+ }
+ }
+}
+
+TrackPart *TrackPart::get_link(unsigned i) const
+{
+ if(i>=2)
+ throw InvalidParameterValue("Index out of range");
+ return links[i];
+}
+
+
+TrackPart::Loader::Loader(TrackPart &p):
+ Msp::DataFile::BasicLoader<TrackPart>(p)
+{
+ add("start", &Loader::start);
+ add("length", &TrackPart::length);
+ add("radius", &TrackPart::radius);
+ add("path", &TrackPart::path);
+ add("dead_end", &TrackPart::dead_end);
+}
+
+void TrackPart::Loader::finish()
+{
+ if(obj.radius)
+ {
+ obj.length *= M_PI/180;
+ obj.radius /= 1000;
+ }
+ else
+ obj.length /= 1000;
+
+ obj.pos.x /= 1000;
+ obj.pos.y /= 1000;
+ obj.dir *= M_PI/180;
+}
+
+void TrackPart::Loader::start(float x, float y, float d)
+{
+ obj.pos = Point(x, y);
+ obj.dir = d;
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_TRACKPART_H_
+#define LIBR2C2_TRACKPART_H_
+
+#include <msp/datafile/loader.h>
+#include "geometry.h"
+
+namespace R2C2 {
+
+class TrackPart
+{
+public:
+ class Loader: public Msp::DataFile::BasicLoader<TrackPart>
+ {
+ public:
+ Loader(TrackPart &);
+ private:
+ virtual void finish();
+ void start(float, float, float);
+ };
+
+private:
+ Point pos;
+ float dir;
+ float length;
+ float radius;
+ unsigned path;
+ bool dead_end;
+ TrackPart *links[2];
+
+public:
+ TrackPart();
+
+ float get_length() const;
+ bool is_curved() const { return radius; }
+ TrackPoint get_point(float) const;
+ unsigned get_path() const { return path; }
+ bool is_dead_end() const { return dead_end; }
+ void check_link(TrackPart &);
+ TrackPart *get_link(unsigned) const;
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <cmath>
+#include "tracktype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+TrackType::TrackType(const ArticleNumber &an):
+ art_nr(an),
+ double_address(false),
+ autofit_preference(1)
+{ }
+
+float TrackType::get_total_length() const
+{
+ return get_path_length(-1);
+}
+
+float TrackType::get_path_length(int p) const
+{
+ float len = 0;
+ for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
+ if(p<0 || i->get_path()==static_cast<unsigned>(p))
+ len += i->get_length();
+ return len;
+}
+
+unsigned TrackType::get_paths() const
+{
+ unsigned mask = 0;
+ for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
+ mask |= 1<<i->get_path();
+ return mask;
+}
+
+unsigned TrackType::get_n_paths() const
+{
+ unsigned n = 0;
+ for(unsigned mask = get_paths(); mask; ++n)
+ mask &= mask-1;
+ return n;
+}
+
+bool TrackType::is_turnout() const
+{
+ return endpoints.size()>2;
+}
+
+bool TrackType::is_dead_end() const
+{
+ return endpoints.size()<2;
+}
+
+const TrackType::Endpoint &TrackType::get_endpoint(unsigned i) const
+{
+ if(i>=endpoints.size())
+ throw InvalidParameterValue("Endpoint index out of range");
+
+ return endpoints[i];
+}
+
+TrackPoint TrackType::get_point(unsigned epi, unsigned path, float d) const
+{
+ if(epi>=endpoints.size())
+ throw InvalidParameterValue("Endpoint index out of range");
+
+ const TrackPart *part = 0;
+ unsigned part_ep = 0;
+ for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
+ {
+ if((endpoints[epi].paths&(1<<path)) && i->get_path()!=path)
+ continue;
+
+ unsigned n_part_eps = (i->is_dead_end() ? 1 : 2);
+ for(unsigned j=0; j<n_part_eps; ++j)
+ {
+ TrackPoint p = i->get_point(j ? i->get_length() : 0);
+ float dx = p.pos.x-endpoints[epi].pos.x;
+ float dy = p.pos.y-endpoints[epi].pos.y;
+ if(dx*dx+dy*dy<1e-6)
+ {
+ part = &*i;
+ part_ep = j;
+ }
+ }
+ }
+
+ if(!part)
+ throw Exception("Internal error (endpoint does not match any part)");
+
+ while(1)
+ {
+ float plen = part->get_length();
+ if(d<=plen)
+ {
+ if(part_ep==1)
+ d = plen-d;
+ TrackPoint p = part->get_point(d);
+ if(part_ep==1)
+ p.dir += M_PI;
+ return p;
+ }
+ else
+ {
+ d -= plen;
+ TrackPart *next = part->get_link(1-part_ep);
+ if(!next)
+ throw InvalidParameterValue("Distance out of range");
+ part_ep = (next->get_link(0)==part ? 0 : 1);
+ part = next;
+ }
+ }
+}
+
+void TrackType::collect_endpoints()
+{
+ endpoints.clear();
+
+ for(vector<TrackPart>::iterator i=parts.begin(); i!=parts.end(); ++i)
+ {
+ for(vector<TrackPart>::iterator j=i; ++j!=parts.end();)
+ i->check_link(*j);
+
+ unsigned n_part_eps = (i->is_dead_end() ? 1 : 2);
+ for(unsigned j=0; j<n_part_eps; ++j)
+ if(!i->get_link(j))
+ {
+ TrackPoint p = i->get_point(j ? i->get_length() : 0);
+ if(j==0)
+ p.dir += M_PI;
+
+ bool found = false;
+ for(vector<Endpoint>::iterator k=endpoints.begin(); k!=endpoints.end(); ++k)
+ {
+ float dx = k->pos.x-p.pos.x;
+ float dy = k->pos.y-p.pos.y;
+
+ float da = k->dir-p.dir;
+ while(da>M_PI)
+ da -= M_PI*2;
+ while(da<-M_PI)
+ da += M_PI*2;
+
+ if(dx*dx+dy*dy<1e-6 && da>-0.01 && da<0.01)
+ {
+ k->paths |= 1<<i->get_path();
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ endpoints.push_back(Endpoint(p.pos.x, p.pos.y, p.dir, 1<<i->get_path()));
+ }
+ }
+}
+
+TrackType::Endpoint::Endpoint(float x, float y, float d, unsigned p):
+ pos(x, y),
+ dir(d),
+ paths(p)
+{ }
+
+
+TrackType::Loader::Loader(TrackType &t):
+ Msp::DataFile::BasicLoader<TrackType>(t)
+{
+ add("autofit_preference", &TrackType::autofit_preference);
+ add("description", &TrackType::description);
+ add("double_address", &TrackType::double_address);
+ add("part", &Loader::part);
+}
+
+void TrackType::Loader::finish()
+{
+ obj.collect_endpoints();
+}
+
+void TrackType::Loader::part()
+{
+ TrackPart p;
+ load_sub(p);
+ obj.parts.push_back(p);
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_TRACKTYPE_H_
+#define LIBR2C2_TRACKTYPE_H_
+
+#include <msp/datafile/loader.h>
+#include "articlenumber.h"
+#include "geometry.h"
+#include "trackpart.h"
+
+namespace R2C2 {
+
+class TrackType
+{
+public:
+ struct Endpoint
+ {
+ Point pos;
+ float dir; // Direction outwards from the endpoint
+ unsigned paths;
+
+ Endpoint(float, float, float, unsigned);
+ };
+
+ class Loader: public Msp::DataFile::BasicLoader<TrackType>
+ {
+ public:
+ Loader(TrackType &);
+ private:
+ virtual void finish();
+ void part();
+ void position(float, float, float);
+ };
+
+private:
+ ArticleNumber art_nr;
+ std::string description;
+ std::vector<TrackPart> parts;
+ std::vector<Endpoint> endpoints;
+ bool double_address;
+ unsigned autofit_preference;
+
+public:
+ TrackType(const ArticleNumber &);
+
+ const ArticleNumber &get_article_number() const { return art_nr; }
+ const std::string &get_description() const { return description; }
+ float get_total_length() const;
+ float get_path_length(int) const;
+ unsigned get_paths() const;
+ unsigned get_n_paths() const;
+ bool is_turnout() const;
+ bool is_dead_end() const;
+ bool is_double_address() const { return double_address; }
+ unsigned get_autofit_preference() const { return autofit_preference; }
+ const std::vector<TrackPart> &get_parts() const { return parts; }
+ const std::vector<Endpoint> &get_endpoints() const { return endpoints; }
+ const Endpoint &get_endpoint(unsigned) const;
+ TrackPoint get_point(unsigned, unsigned, float) const;
+
+private:
+ void collect_endpoints();
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <cmath>
+#include <msp/strings/formatter.h>
+#include <msp/time/units.h>
+#include <msp/time/utils.h>
+#include "aicontrol.h"
+#include "catalogue.h"
+#include "driver.h"
+#include "layout.h"
+#include "route.h"
+#include "simplecontroller.h"
+#include "timetable.h"
+#include "trackiter.h"
+#include "tracktype.h"
+#include "train.h"
+#include "vehicle.h"
+#include "vehicletype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace {
+
+struct SetFlag
+{
+ bool &flag;
+
+ SetFlag(bool &f): flag(f) { flag = true; }
+ ~SetFlag() { flag = false; }
+};
+
+}
+
+
+namespace R2C2 {
+
+Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
+ layout(l),
+ loco_type(t),
+ address(a),
+ protocol(p),
+ priority(0),
+ yielding_to(0),
+ cur_blocks_end(blocks.end()),
+ clear_blocks_end(blocks.end()),
+ pending_block(0),
+ reserving(false),
+ advancing(false),
+ controller(new AIControl(*this, new SimpleController)),
+ timetable(0),
+ active(false),
+ current_speed_step(0),
+ speed_changing(false),
+ reverse(false),
+ functions(0),
+ end_of_route(false),
+ status("Unplaced"),
+ travel_dist(0),
+ pure_speed(false),
+ real_speed(layout.get_driver().get_protocol_speed_steps(protocol)+1),
+ accurate_position(false),
+ overshoot_dist(false)
+{
+ if(!loco_type.is_locomotive())
+ throw InvalidParameterValue("Initial vehicle must be a locomotive");
+
+ vehicles.push_back(new Vehicle(layout, loco_type));
+
+ layout.add_train(*this);
+
+ layout.get_driver().add_loco(address, protocol);
+ layout.get_driver().signal_loco_speed.connect(sigc::mem_fun(this, &Train::loco_speed_event));
+ layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event));
+
+ layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
+ layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Train::sensor_event));
+
+ layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
+
+ const set<Track *> &tracks = layout.get_tracks();
+ for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ if((*i)->get_turnout_id())
+ (*i)->signal_path_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changed), sigc::ref(**i))));
+
+ controller->signal_control_changed.connect(sigc::mem_fun(this, &Train::control_changed));
+}
+
+Train::~Train()
+{
+ delete controller;
+ delete timetable;
+ for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
+ delete *i;
+ layout.remove_train(*this);
+}
+
+void Train::set_name(const string &n)
+{
+ name = n;
+
+ signal_name_changed.emit(name);
+}
+
+void Train::set_priority(int p)
+{
+ priority = p;
+}
+
+void Train::yield_to(const Train &t)
+{
+ yielding_to = &t;
+}
+
+void Train::add_vehicle(const VehicleType &vt)
+{
+ Vehicle *veh = new Vehicle(layout, vt);
+ vehicles.back()->attach_back(*veh);
+ vehicles.push_back(veh);
+}
+
+void Train::remove_vehicle(unsigned i)
+{
+ if(i>=vehicles.size())
+ throw InvalidParameterValue("Vehicle index out of range");
+ if(i==0)
+ throw InvalidParameterValue("Can't remove the locomotive");
+ delete vehicles[i];
+ vehicles.erase(vehicles.begin()+i);
+ if(i<vehicles.size())
+ vehicles[i-1]->attach_back(*vehicles[i]);
+}
+
+unsigned Train::get_n_vehicles() const
+{
+ return vehicles.size();
+}
+
+Vehicle &Train::get_vehicle(unsigned i)
+{
+ if(i>=vehicles.size())
+ throw InvalidParameterValue("Vehicle index out of range");
+ return *vehicles[i];
+}
+
+const Vehicle &Train::get_vehicle(unsigned i) const
+{
+ if(i>=vehicles.size())
+ throw InvalidParameterValue("Vehicle index out of range");
+ return *vehicles[i];
+}
+
+void Train::set_control(const string &n, float v)
+{
+ controller->set_control(n, v);
+}
+
+void Train::set_active(bool a)
+{
+ if(a==active)
+ return;
+ if(!a && controller->get_speed())
+ throw InvalidState("Can't deactivate while moving");
+
+ active = a;
+ if(active)
+ {
+ stop_timeout = Time::TimeStamp();
+ reserve_more();
+ }
+ else
+ {
+ stop_timeout = Time::now()+2*Time::sec;
+ set_status("Stopped");
+ }
+}
+
+void Train::set_function(unsigned func, bool state)
+{
+ if(!loco_type.get_functions().count(func))
+ throw InvalidParameterValue("Invalid function");
+ if(func<5)
+ layout.get_driver().set_loco_function(address, func, state);
+ else
+ layout.get_driver().set_loco_function(address+1, func-4, state);
+}
+
+float Train::get_control(const string &ctrl) const
+{
+ return controller->get_control(ctrl).value;
+}
+
+float Train::get_speed() const
+{
+ return controller->get_speed();
+}
+
+bool Train::get_function(unsigned func) const
+{
+ return (functions>>func)&1;
+}
+
+void Train::set_timetable(Timetable *tt)
+{
+ delete timetable;
+ timetable = tt;
+}
+
+bool Train::set_route(const Route *r)
+{
+ free_noncritical_blocks();
+
+ Route *lead = 0;
+ if(r && !blocks.empty())
+ {
+ TrackIter first = blocks.front().track_iter();
+ TrackIter next = blocks.back().next().track_iter();
+ if(!r->has_track(*next))
+ {
+ lead = Route::find(next, *r);
+ if(!lead)
+ return false;
+ create_lead_route(lead, lead);
+ routes.push_front(lead);
+ }
+ else if(!r->has_track(*first))
+ lead = create_lead_route(0, r);
+ }
+
+ routes.clear();
+ if(lead)
+ routes.push_back(lead);
+ if(r)
+ routes.push_back(r);
+ end_of_route = false;
+
+ reserve_more();
+
+ signal_route_changed.emit(get_route());
+
+ return true;
+}
+
+bool Train::go_to(Track &to)
+{
+ for(BlockList::const_iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
+ if((*i)->has_track(to))
+ {
+ signal_arrived.emit();
+ return set_route(0);
+ }
+
+ free_noncritical_blocks();
+
+ TrackIter next = blocks.back().next().track_iter();
+
+ Route *route = Route::find(next, to);
+ if(!route)
+ return false;
+ create_lead_route(route, route);
+ return set_route(route);
+}
+
+bool Train::divert(Track &from)
+{
+ if(!from.get_turnout_id())
+ throw InvalidParameterValue("Can't divert from a non-turnout");
+ if(routes.empty())
+ return false;
+
+ unsigned path = 0;
+ unsigned entry = 0;
+ list<RouteRef>::iterator route = routes.begin();
+
+ // Follow our routes to find out where we're entering the turnout
+ for(TrackLoopIter track = blocks.front().track_iter();;)
+ {
+ if(!advance_route(route, *track))
+ return false;
+
+ if(&*track==&from)
+ {
+ Block &block = track->get_block();
+ if(block.get_train()==this && !free_block(block))
+ return false;
+
+ int route_path = route->route->get_turnout(from.get_turnout_id());
+
+ // Check that more than one path is available
+ unsigned ep_paths = track.endpoint().paths;
+ if(!(ep_paths&(ep_paths-1)))
+ return false;
+
+ // Choose some other path
+ for(int i=0; ep_paths>>i; ++i)
+ if((ep_paths&(1<<i)) && i!=route_path)
+ {
+ path = i;
+ break;
+ }
+
+ entry = track.entry();
+ break;
+ }
+
+ track = track.next(route->route->get_path(*track));
+
+ if(!track || track.looped())
+ return false;
+ }
+
+ TrackIter track = TrackIter(&from, entry).next(path);
+ if(!track)
+ return false;
+
+ set<Track *> tracks;
+ for(list<RouteRef>::iterator i=routes.begin(); i!=routes.end(); ++i)
+ tracks.insert(i->route->get_tracks().begin(), i->route->get_tracks().end());
+ RefPtr<Route> diversion = Route::find(track, tracks);
+ if(!diversion)
+ return false;
+
+ diversion->set_name("Diversion");
+ diversion->add_track(from);
+ diversion->set_turnout(from.get_turnout_id(), path);
+
+ if(!is_valid_diversion(*diversion, TrackIter(&from, entry)))
+ return false;
+
+ // Follow the diversion route until we get back to the original route
+ list<RouteRef>::iterator end = routes.end();
+ while(1)
+ {
+ for(list<RouteRef>::iterator i=route; (end==routes.end() && i!=routes.end()); ++i)
+ if(i->route->has_track(*track))
+ end = i;
+
+ if(end!=routes.end())
+ break;
+ else if(!diversion->has_track(*track))
+ throw LogicError("Pathfinder returned a bad route");
+
+ track = track.next(diversion->get_path(*track));
+ }
+
+ if(route==end)
+ // We are rejoining the same route we diverted from, duplicate it
+ routes.insert(end, *route);
+ else
+ {
+ ++route;
+ routes.erase(route, end);
+ }
+ routes.insert(end, RouteRef(diversion.release(), from.get_turnout_id()));
+
+ return true;
+}
+
+const Route *Train::get_route() const
+{
+ if(routes.empty())
+ return 0;
+ return routes.front().route;
+}
+
+void Train::place(Block &block, unsigned entry)
+{
+ if(controller->get_speed())
+ throw InvalidState("Must be stopped before placing");
+
+ release_blocks();
+
+ set_active(false);
+ accurate_position = false;
+
+ if(!block.reserve(this))
+ {
+ set_status("Unplaced");
+ return;
+ }
+
+ blocks.push_back(BlockIter(&block, entry));
+ if(reverse)
+ {
+ TrackIter track = BlockIter(&block, entry).reverse().track_iter();
+ vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_BUFFER);
+ }
+ else
+ {
+ const Block::Endpoint &bep = block.get_endpoint(entry);
+ vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
+ }
+}
+
+void Train::unplace()
+{
+ if(controller->get_speed())
+ throw InvalidState("Must be stopped before unplacing");
+
+ release_blocks();
+
+ set_active(false);
+ accurate_position = false;
+
+ for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
+ (*i)->unplace();
+
+ set_status("Unplaced");
+}
+
+bool Train::free_block(Block &block)
+{
+ float margin = 10*layout.get_catalogue().get_scale();
+ if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3+margin)
+ return false;
+
+ unsigned nsens = 0;
+ for(BlockList::iterator i=cur_blocks_end; i!=blocks.end(); ++i)
+ {
+ if(i->block()==&block)
+ {
+ if(nsens<1)
+ return false;
+ release_blocks(i, blocks.end());
+ return true;
+ }
+ else if((*i)->get_sensor_id())
+ ++nsens;
+ }
+
+ return false;
+}
+
+void Train::free_noncritical_blocks()
+{
+ if(blocks.empty())
+ return;
+
+ if(controller->get_speed()==0)
+ {
+ release_blocks(cur_blocks_end, blocks.end());
+ return;
+ }
+
+ float margin = 10*layout.get_catalogue().get_scale();
+ float min_dist = controller->get_braking_distance()*1.3+margin;
+
+ Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
+
+ TrackIter track(veh.get_track(), veh.get_entry());
+ BlockList::iterator block = blocks.begin();
+ bool in_rsv = false;
+ while(block!=blocks.end() && !(*block)->has_track(*track))
+ {
+ ++block;
+ if(block==cur_blocks_end)
+ in_rsv = true;
+ }
+
+ float dist = veh.get_offset();
+ if(reverse)
+ track.reverse();
+ else
+ dist = track->get_type().get_path_length(track->get_active_path())-dist;
+ dist -= veh.get_type().get_length()/2;
+
+ bool nsens = 0;
+ while(1)
+ {
+ track = track.next();
+
+ if(!(*block)->has_track(*track))
+ {
+ ++block;
+ if(block==cur_blocks_end)
+ in_rsv = true;
+ if(block==blocks.end())
+ return;
+
+ if(dist>min_dist && nsens>0)
+ {
+ release_blocks(block, blocks.end());
+ return;
+ }
+
+ if(in_rsv && (*block)->get_sensor_id())
+ ++nsens;
+ }
+
+ dist += track->get_type().get_path_length(track->get_active_path());
+ }
+}
+
+int Train::get_entry_to_block(Block &block) const
+{
+ for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ if(i->block()==&block)
+ return i->entry();
+ return -1;
+}
+
+float Train::get_reserved_distance() const
+{
+ return get_reserved_distance_until(0, false);
+}
+
+void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
+{
+ if(!active && stop_timeout && t>=stop_timeout)
+ {
+ release_blocks(cur_blocks_end, blocks.end());
+ stop_timeout = Time::TimeStamp();
+ }
+
+ Driver &driver = layout.get_driver();
+
+ if(timetable)
+ timetable->tick(t);
+ controller->tick(dt);
+ float speed = controller->get_speed();
+ unsigned speed_step = find_speed_step(speed);
+
+ if(controller->get_reverse()!=reverse)
+ {
+ reverse = controller->get_reverse();
+ driver.set_loco_reverse(address, reverse);
+
+ release_blocks(cur_blocks_end, blocks.end());
+ reverse_blocks(blocks);
+
+ reserve_more();
+ }
+ if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
+ {
+ speed_changing = true;
+ driver.set_loco_speed(address, speed_step);
+
+ pure_speed = false;
+
+ if(speed_step)
+ set_status(format("Traveling %d kmh", get_travel_speed()));
+ else
+ set_status("Waiting");
+ }
+
+ if(speed)
+ {
+ if(!active)
+ set_active(true);
+
+ Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
+ Track *track = vehicle.get_track();
+
+ bool ok = false;
+ for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
+ ok = (*i)->has_track(*track);
+
+ float d;
+ if(real_speed.size()>1)
+ d = get_real_speed(current_speed_step)*(dt/Time::sec);
+ else
+ d = speed*(dt/Time::sec);
+ if(ok)
+ {
+ SetFlag setf(advancing);
+ vehicle.advance(reverse ? -d : d);
+ }
+ else if(accurate_position)
+ {
+ overshoot_dist += d;
+ if(overshoot_dist>40*layout.get_catalogue().get_scale())
+ {
+ layout.emergency(name+" has not arrived at sensor");
+ accurate_position = false;
+ }
+ }
+ }
+ else if(end_of_route && cur_blocks_end==blocks.end())
+ {
+ set_active(false);
+ signal_arrived.emit();
+ set_route(0);
+ }
+
+ if(!blocks.empty() && !blocks.front()->get_sensor_id())
+ {
+ float dist = get_reserved_distance_until(&*blocks.front(), true);
+
+ if(dist>10*layout.get_catalogue().get_scale())
+ {
+ blocks.front()->reserve(0);
+ blocks.pop_front();
+ }
+ }
+}
+
+void Train::save(list<DataFile::Statement> &st) const
+{
+ st.push_back((DataFile::Statement("name"), name));
+
+ st.push_back((DataFile::Statement("priority"), priority));
+
+ for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
+ if(i!=vehicles.begin())
+ st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
+
+ for(unsigned i=0; i<real_speed.size(); ++i)
+ if(real_speed[i].weight)
+ st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
+
+ if(!blocks.empty() && cur_blocks_end!=blocks.begin())
+ {
+ BlockList blks(blocks.begin(), BlockList::const_iterator(cur_blocks_end));
+ if(reverse)
+ reverse_blocks(blks);
+
+ BlockIter prev = blks.front().flip();
+ st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
+
+ for(BlockList::const_iterator i=blks.begin(); i!=blks.end(); ++i)
+ st.push_back((DataFile::Statement("block"), (*i)->get_id()));
+ }
+
+ if(!routes.empty())
+ {
+ list<RouteRef>::const_iterator i = routes.begin();
+ for(; (i!=routes.end() && i->route->is_temporary()); ++i) ;
+ if(i!=routes.end())
+ st.push_back((DataFile::Statement("route"), i->route->get_name()));
+ }
+
+ if(timetable)
+ {
+ DataFile::Statement ss("timetable");
+ timetable->save(ss.sub);
+ st.push_back(ss);
+ }
+}
+
+void Train::control_changed(const Controller::Control &ctrl)
+{
+ signal_control_changed.emit(ctrl.name, ctrl.value);
+}
+
+void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
+{
+ if(addr==address)
+ {
+ current_speed_step = speed;
+ speed_changing = false;
+ pure_speed = false;
+ }
+}
+
+void Train::loco_func_event(unsigned addr, unsigned func, bool state)
+{
+ if(addr==address || (addr==address+1 && loco_type.get_max_function()>4))
+ {
+ if(addr==address+1)
+ func += 4;
+ if(state)
+ functions |= 1<<func;
+ else
+ functions &= ~(1<<func);
+
+ signal_function_changed.emit(func, state);
+ }
+}
+
+void Train::sensor_event(unsigned addr, bool state)
+{
+ if(state)
+ {
+ // Find the first sensor block from our reserved blocks that isn't this sensor
+ BlockList::iterator end;
+ unsigned result = 0;
+ for(end=cur_blocks_end; end!=blocks.end(); ++end)
+ if((*end)->get_sensor_id())
+ {
+ if((*end)->get_sensor_id()!=addr)
+ {
+ if(result==0)
+ result = 2;
+ else if(result==1)
+ break;
+ }
+ else if(result==0)
+ result = 1;
+ else if(result==2)
+ result = 3;
+ }
+
+ if(result==1)
+ {
+ // Compute speed and update related state
+ float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
+
+ if(pure_speed)
+ {
+ if(current_speed_step>0)
+ {
+ RealSpeed &rs = real_speed[current_speed_step];
+ rs.add(travel_dist/travel_time_secs, travel_time_secs);
+ }
+ set_status(format("Traveling %d kmh", get_travel_speed()));
+ }
+
+ travel_dist = 0;
+ for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
+ {
+ travel_dist += (*j)->get_path_length(j->entry());
+
+ if((*j)->get_sensor_id()==addr && !advancing)
+ {
+ TrackIter track = j->track_iter();
+ if(reverse)
+ {
+ track = track.flip();
+ vehicles.back()->place(*track, track.entry(), 0, Vehicle::BACK_AXLE);
+ }
+ else
+ vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_AXLE);
+ }
+ }
+ last_entry_time = Time::now();
+ pure_speed = true;
+ accurate_position = true;
+ overshoot_dist = 0;
+
+ // Check if we've reached the next route
+ if(routes.size()>1)
+ {
+ const Route &route = *(++routes.begin())->route;
+ for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
+ if(route.has_track(*j->track_iter()))
+ {
+ routes.pop_front();
+ // XXX Exceptions?
+ signal_route_changed.emit(routes.front().route);
+ break;
+ }
+ }
+
+ // Move blocks up to the next sensor to our current blocks
+ cur_blocks_end = end;
+
+ // Try to get more blocks if we're moving
+ if(active)
+ reserve_more();
+ }
+ else if(result==3)
+ layout.emergency("Sensor for "+name+" triggered out of order");
+ }
+ else
+ {
+ const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
+
+ // Find the first sensor in our current blocks that's still active
+ BlockList::iterator end = blocks.begin();
+ for(BlockList::iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
+ {
+ if((*i)->has_track(*veh.get_track()))
+ break;
+ if((*i)->get_sensor_id())
+ {
+ if(layout.get_driver().get_sensor((*i)->get_sensor_id()))
+ break;
+ else
+ {
+ end = i;
+ ++end;
+ }
+ }
+ }
+
+ if(end!=blocks.begin() && end!=cur_blocks_end)
+ // Free blocks up to the last inactive sensor
+ release_blocks(blocks.begin(), end);
+ }
+}
+
+void Train::turnout_path_changed(Track &track)
+{
+ for(list<BlockIter>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ if((*i)->get_turnout_id()==track.get_turnout_id() && !reserving)
+ check_turnout_paths(false);
+}
+
+void Train::halt_event(bool h)
+{
+ if(h)
+ accurate_position = false;
+}
+
+void Train::block_reserved(const Block &block, const Train *train)
+{
+ if(&block==pending_block && !train && !reserving)
+ reserve_more();
+}
+
+void Train::reserve_more()
+{
+ if(!active || blocks.empty() || end_of_route)
+ return;
+
+ BlockIter start = blocks.back();
+
+ pending_block = 0;
+
+ // See how many sensor blocks and how much track we already have
+ unsigned nsens = 0;
+ float dist = 0;
+ for(BlockList::const_iterator i=cur_blocks_end; i!=blocks.end(); ++i)
+ {
+ if((*i)->get_sensor_id())
+ ++nsens;
+ if(nsens>0)
+ dist += (*i)->get_path_length(i->entry());
+ }
+
+ list<RouteRef>::iterator cur_route = routes.begin();
+ advance_route(cur_route, *start.track_iter());
+
+ float approach_margin = 50*layout.get_catalogue().get_scale();
+ float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
+
+ BlockIter block = start;
+ list<BlockIter>::iterator good_end = blocks.end();
+ Track *divert_track = 0;
+ bool try_divert = false;
+ Train *blocking_train = 0;
+ BlockList contested_blocks;
+
+ SetFlag setf(reserving);
+
+ while(1)
+ {
+ BlockIter last = block;
+ block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
+ if(!block || block->get_endpoints().size()<2)
+ {
+ if(!blocking_train)
+ good_end = blocks.end();
+ break;
+ }
+
+ TrackIter track = block.track_iter();
+
+ if(cur_route!=routes.end())
+ {
+ if(!advance_route(cur_route, *track))
+ {
+ // Keep the blocks if we arrived at the end of the route
+ if(!blocking_train)
+ {
+ good_end = blocks.end();
+ end_of_route = true;
+ }
+ break;
+ }
+ }
+
+ if(block->get_turnout_id() && !last->get_turnout_id())
+ {
+ /* We can keep the blocks if we arrive at a turnout from a non-turnout
+ block. Having a turnout block as our last reserved block is not good
+ as it would limit our diversion possibilities for little benefit. */
+ good_end = blocks.end();
+ if(nsens>=3 && dist>=min_dist)
+ break;
+ }
+
+ if(blocking_train)
+ {
+ if(block->get_train()!=blocking_train)
+ {
+ if(blocking_train->free_block(*contested_blocks.back()))
+ {
+ // Roll back and start actually reserving the blocks
+ block = blocks.back();
+ cur_route = routes.begin();
+ advance_route(cur_route, *block.track_iter().track());
+ if(blocking_train->get_priority()==priority)
+ blocking_train->yield_to(*this);
+ blocking_train = 0;
+ continue;
+ }
+ else
+ {
+ yield_to(*blocking_train);
+ pending_block = contested_blocks.front().block();
+ try_divert = divert_track;
+ break;
+ }
+ }
+ else
+ {
+ contested_blocks.push_back(block);
+ continue;
+ }
+ }
+
+ bool reserved = block->reserve(this);
+ if(!reserved)
+ {
+ /* We've found another train. If it wants to exit the block from the
+ same endpoint we're trying to enter from or the other way around,
+ treat it as coming towards us. Otherwise treat it as going in the
+ same direction. */
+ Train *other_train = block->get_train();
+ int other_entry = other_train->get_entry_to_block(*block);
+ if(other_entry<0)
+ throw LogicError("Block reservation inconsistency");
+
+ unsigned exit = block.reverse().entry();
+ unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
+ bool entry_conflict = (block.entry()==other_exit);
+ bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
+ if(!entry_conflict && !last->get_turnout_id())
+ /* The other train is not coming to the blocks we're holding, so we
+ can keep them. */
+ good_end = blocks.end();
+
+ int other_prio = other_train->get_priority();
+
+ if(!entry_conflict && !exit_conflict && other_prio<priority)
+ {
+ /* Ask a lesser priority train going to the same direction to free
+ the block for us */
+ if(other_train->free_block(*block))
+ reserved = block->reserve(this);
+ }
+ else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
+ {
+ /* A lesser priority train is coming at us, we must ask it to free
+ enough blocks to get clear of it to avoid a potential deadlock */
+ blocking_train = other_train;
+ contested_blocks.clear();
+ contested_blocks.push_back(block);
+ continue;
+ }
+ else if(divert_track && (entry_conflict || exit_conflict))
+ // We are blocked, but there's a diversion possibility
+ try_divert = true;
+
+ if(!reserved)
+ {
+ pending_block = &*block;
+ break;
+ }
+ }
+
+ if(block->get_turnout_id())
+ {
+ const TrackType::Endpoint &track_ep = track.endpoint();
+ bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
+
+ if(multiple_paths && cur_route!=routes.end() && cur_route->diversion!=block->get_turnout_id())
+ /* There's multiple paths to be taken and we are on a route - take
+ note of the diversion possibility */
+ divert_track = &*track;
+ }
+
+ if(!contested_blocks.empty() && contested_blocks.front()==block)
+ contested_blocks.pop_front();
+
+ blocks.push_back(block);
+
+ if(cur_blocks_end==blocks.end())
+ --cur_blocks_end;
+ if(clear_blocks_end==blocks.end())
+ --clear_blocks_end;
+ if(good_end==blocks.end())
+ --good_end;
+
+ if(block->get_sensor_id())
+ ++nsens;
+ if(nsens>0)
+ dist += block->get_path_length(block.entry());
+ }
+
+ // Unreserve blocks that were not good
+ release_blocks(good_end, blocks.end());
+
+ if(blocks.back()!=start)
+ // We got some new blocks, so no longer need to yield
+ yielding_to = 0;
+
+ check_turnout_paths(true);
+
+ // Make any sensorless blocks at the beginning immediately current
+ while(cur_blocks_end!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
+ ++cur_blocks_end;
+
+ if(try_divert && divert(*divert_track))
+ reserve_more();
+}
+
+void Train::check_turnout_paths(bool set)
+{
+ if(clear_blocks_end==blocks.end())
+ return;
+
+ for(list<BlockIter>::iterator i=clear_blocks_end; i!=blocks.end(); ++i)
+ {
+ if((*i)->get_turnout_id())
+ {
+ TrackIter track = i->track_iter();
+ const TrackType::Endpoint &track_ep = track.endpoint();
+
+ unsigned path = 0;
+ list<BlockIter>::iterator j = i;
+ if(++j!=blocks.end())
+ {
+ TrackIter rev = j->track_iter().flip();
+ unsigned mask = rev.endpoint().paths&track_ep.paths;
+ for(path=0; mask>1; mask>>=1, ++path) ;
+ }
+ else
+ return;
+
+ if(path!=track->get_active_path())
+ {
+ if(set)
+ track->set_active_path(path);
+
+ /* Check again, in case the driver was able to service the request
+ instantly */
+ if(!set || path!=track->get_active_path())
+ continue;
+ }
+ }
+
+ if(i==clear_blocks_end)
+ ++clear_blocks_end;
+ }
+}
+
+float Train::get_reserved_distance_until(const Block *until_block, bool back) const
+{
+ if(blocks.empty())
+ return 0;
+
+ Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
+ const VehicleType &vtype = veh.get_type();
+
+ TrackIter track(veh.get_track(), veh.get_entry());
+ if(!track) // XXX Probably unnecessary
+ return 0;
+
+ BlockList::const_iterator block = blocks.begin();
+ while(block!=clear_blocks_end && !(*block)->has_track(*track))
+ ++block;
+ if(block==clear_blocks_end || &**block==until_block)
+ return 0;
+
+ float result = veh.get_offset();
+ if(reverse!=back)
+ track = track.reverse();
+ else
+ result = track->get_type().get_path_length(track->get_active_path())-result;
+ result -= vtype.get_length()/2;
+
+ while(1)
+ {
+ track = track.next();
+ if(!track)
+ break;
+
+ if(!(*block)->has_track(*track))
+ {
+ if(back)
+ {
+ if(block==blocks.begin())
+ break;
+ --block;
+ }
+ else
+ {
+ ++block;
+ if(block==clear_blocks_end)
+ break;
+ }
+
+ if(&**block==until_block)
+ break;
+ }
+
+ result += track->get_type().get_path_length(track->get_active_path());
+ }
+
+ return result;
+}
+
+float Train::get_real_speed(unsigned i) const
+{
+ if(i==0)
+ return 0;
+ if(real_speed[i].weight)
+ return real_speed[i].speed;
+
+ unsigned low;
+ unsigned high;
+ for(low=i; low>0; --low)
+ if(real_speed[low].weight)
+ break;
+ for(high=i; high+1<real_speed.size(); ++high)
+ if(real_speed[high].weight)
+ break;
+
+ if(real_speed[high].weight)
+ {
+ if(real_speed[low].weight)
+ {
+ float f = float(i-low)/(high-low);
+ return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
+ }
+ else
+ return real_speed[high].speed*float(i)/high;
+ }
+ else if(real_speed[low].weight)
+ return real_speed[low].speed*float(i)/low;
+ else
+ return 0;
+}
+
+unsigned Train::find_speed_step(float real) const
+{
+ if(real_speed.size()<=1)
+ return 0;
+ if(real<=real_speed[1].speed*0.5)
+ return 0;
+
+ unsigned low = 0;
+ unsigned high = 0;
+ unsigned last = 0;
+ for(unsigned i=0; (!high && i<real_speed.size()); ++i)
+ if(real_speed[i].weight)
+ {
+ last = i;
+ if(real_speed[i].speed<real)
+ low = i;
+ else
+ high = i;
+ }
+ if(!high)
+ {
+ unsigned limit = real_speed.size()/5;
+ if(!low)
+ {
+ if(real)
+ return limit;
+ else
+ return 0;
+ }
+ return min(min(static_cast<unsigned>(low*real/real_speed[low].speed), real_speed.size()-1), last+limit);
+ }
+
+ float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
+ return static_cast<unsigned>(low*(1-f)+high*f+0.5);
+}
+
+float Train::get_travel_speed() const
+{
+ float speed = get_real_speed(current_speed_step);
+ float scale = layout.get_catalogue().get_scale();
+ return static_cast<int>(round(speed/scale*3.6/5))*5;
+}
+
+void Train::set_status(const string &s)
+{
+ status = s;
+ signal_status_changed.emit(s);
+}
+
+void Train::release_blocks()
+{
+ release_blocks(blocks.begin(), blocks.end());
+}
+
+void Train::release_blocks(BlockList::iterator begin, BlockList::iterator end)
+{
+ while(begin!=end)
+ {
+ if(begin==cur_blocks_end)
+ cur_blocks_end = end;
+ if(begin==clear_blocks_end)
+ clear_blocks_end = end;
+
+ Block &block = **begin;
+ blocks.erase(begin++);
+ block.reserve(0);
+
+ if(begin==blocks.end())
+ end_of_route = false;
+ }
+}
+
+void Train::reverse_blocks(BlockList &blks) const
+{
+ blks.reverse();
+ for(BlockList::iterator i=blks.begin(); i!=blks.end(); ++i)
+ *i = i->reverse();
+}
+
+bool Train::advance_route(list<RouteRef>::iterator &iter, Track &track)
+{
+ while(iter!=routes.end() && !iter->route->has_track(track))
+ ++iter;
+ if(iter==routes.end())
+ return false;
+
+ list<RouteRef>::iterator next = iter;
+ ++next;
+ if(next!=routes.end() && next->diversion && next->route->has_track(track))
+ iter = next;
+
+ return true;
+}
+
+Route *Train::create_lead_route(Route *lead, const Route *target)
+{
+ if(!lead)
+ {
+ lead = new Route(layout);
+ lead->set_name("Lead");
+ lead->set_temporary(true);
+ }
+
+ set<Track *> tracks;
+ for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ {
+ const set<Track *> &btracks = (*i)->get_tracks();
+ for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
+ if(!target || !target->has_track(**j))
+ tracks.insert(*j);
+ }
+
+ lead->add_tracks(tracks);
+
+ return lead;
+}
+
+bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from)
+{
+ float diversion_len = 0;
+ TrackLoopIter track1 = from;
+ while(diversion.has_track(*track1))
+ {
+ unsigned path = diversion.get_path(*track1);
+ diversion_len += track1->get_type().get_path_length(path);
+
+ track1 = track1.next(path);
+
+ if(track1.looped())
+ return false;
+ }
+
+ list<RouteRef>::iterator route = routes.begin();
+ if(!advance_route(route, *from))
+ return false;
+
+ float route_len = 0;
+ TrackLoopIter track2 = from;
+ while(1)
+ {
+ unsigned path = route->route->get_path(*track2);
+ route_len += track2->get_type().get_path_length(path);
+
+ bool ok = (track2!=from && diversion.has_track(*track2));
+
+ track2 = track2.next(path);
+
+ if(ok)
+ break;
+
+ if(track2.looped())
+ return false;
+
+ if(!advance_route(route, *track2))
+ return false;
+ }
+
+ // Must end up at the same place through both routes
+ if(track2!=track1)
+ return false;
+
+ return diversion_len<route_len*1.2;
+}
+
+
+Train::RouteRef::RouteRef(const Route *r, unsigned d):
+ route(r),
+ diversion(d)
+{ }
+
+
+Train::RealSpeed::RealSpeed():
+ speed(0),
+ weight(0)
+{ }
+
+void Train::RealSpeed::add(float s, float w)
+{
+ speed = (speed*weight+s*w)/(weight+w);
+ weight = min(weight+w, 300.0f);
+}
+
+
+Train::Loader::Loader(Train &t):
+ DataFile::BasicLoader<Train>(t),
+ prev_block(0),
+ blocks_valid(true)
+{
+ add("block", &Loader::block);
+ add("block_hint", &Loader::block_hint);
+ add("name", &Loader::name);
+ add("priority", &Train::priority);
+ add("real_speed", &Loader::real_speed);
+ add("route", &Loader::route);
+ add("timetable", &Loader::timetable);
+ add("vehicle", &Loader::vehicle);
+}
+
+void Train::Loader::finish()
+{
+ if(!obj.blocks.empty())
+ {
+ TrackIter track = obj.blocks.front().track_iter();
+ float offset = 2*obj.layout.get_catalogue().get_scale();
+ obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
+
+ obj.set_status("Stopped");
+ }
+}
+
+void Train::Loader::block(unsigned id)
+{
+ if(!blocks_valid)
+ return;
+
+ Block *blk;
+ try
+ {
+ blk = &obj.layout.get_block(id);
+ }
+ catch(const KeyError &)
+ {
+ blocks_valid = false;
+ return;
+ }
+
+ int entry = -1;
+ if(prev_block)
+ entry = blk->get_endpoint_by_link(*prev_block);
+ if(entry<0)
+ entry = 0;
+
+ blk->reserve(&obj);
+ obj.blocks.push_back(BlockIter(blk, entry));
+
+ if(blk->get_sensor_id())
+ obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
+
+ prev_block = blk;
+}
+
+void Train::Loader::block_hint(unsigned id)
+{
+ try
+ {
+ prev_block = &obj.layout.get_block(id);
+ }
+ catch(const KeyError &)
+ {
+ blocks_valid = false;
+ }
+}
+
+void Train::Loader::name(const string &n)
+{
+ obj.set_name(n);
+}
+
+void Train::Loader::real_speed(unsigned i, float speed, float weight)
+{
+ if(i>=obj.real_speed.size())
+ return;
+ obj.real_speed[i].speed = speed;
+ obj.real_speed[i].weight = weight;
+}
+
+void Train::Loader::route(const string &n)
+{
+ obj.set_route(&obj.layout.get_route(n));
+}
+
+void Train::Loader::timetable()
+{
+ if(obj.timetable)
+ throw InvalidState("A timetable has already been loaded");
+
+ obj.timetable = new Timetable(obj);
+ load_sub(*obj.timetable);
+}
+
+void Train::Loader::vehicle(ArticleNumber art_nr)
+{
+ const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(art_nr);
+ Vehicle *veh = new Vehicle(obj.layout, vtype);
+ obj.vehicles.back()->attach_back(*veh);
+ obj.vehicles.push_back(veh);
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_TRAIN_H_
+#define LIBR2C2_TRAIN_H_
+
+#include <sigc++/signal.h>
+#include <sigc++/trackable.h>
+#include <msp/time/timestamp.h>
+#include "block.h"
+#include "blockiter.h"
+#include "controller.h"
+
+namespace R2C2 {
+
+class ArticleNumber;
+class Route;
+class Timetable;
+class Vehicle;
+class VehicleType;
+
+class Train: public sigc::trackable
+{
+public:
+ class Loader: public Msp::DataFile::BasicLoader<Train>
+ {
+ private:
+ Block *prev_block;
+ bool blocks_valid;
+
+ public:
+ Loader(Train &);
+ private:
+ virtual void finish();
+ void block(unsigned);
+ void block_hint(unsigned);
+ void name(const std::string &);
+ void real_speed(unsigned, float, float);
+ void route(const std::string &);
+ void timetable();
+ void vehicle(ArticleNumber);
+ };
+
+ sigc::signal<void, const std::string &> signal_name_changed;
+ sigc::signal<void, const std::string &, float> signal_control_changed;
+ sigc::signal<void, unsigned, bool> signal_function_changed;
+ sigc::signal<void, const Route *> signal_route_changed;
+ sigc::signal<void> signal_arrived;
+ sigc::signal<void, const std::string &> signal_status_changed;
+
+private:
+ struct RouteRef
+ {
+ const Route *route;
+ unsigned diversion;
+
+ RouteRef(const Route *, unsigned = 0);
+ };
+
+ struct RealSpeed
+ {
+ float speed;
+ float weight;
+
+ RealSpeed();
+ void add(float, float);
+ };
+
+ typedef std::list<BlockIter> BlockList;
+
+ Layout &layout;
+ const VehicleType &loco_type;
+ unsigned address;
+ std::string protocol;
+ std::string name;
+ int priority;
+ const Train *yielding_to;
+ std::vector<Vehicle *> vehicles;
+ BlockList blocks;
+ BlockList::iterator cur_blocks_end;
+ BlockList::iterator clear_blocks_end;
+ Block *pending_block;
+ bool reserving;
+ bool advancing;
+ Controller *controller;
+ Timetable *timetable;
+ bool active;
+ unsigned current_speed_step;
+ bool speed_changing;
+ bool reverse;
+ Msp::Time::TimeStamp stop_timeout;
+ unsigned functions;
+ std::list<RouteRef> routes;
+ bool end_of_route;
+ std::string status;
+
+ Msp::Time::TimeStamp last_entry_time;
+ float travel_dist;
+ bool pure_speed;
+ std::vector<RealSpeed> real_speed;
+ bool accurate_position;
+ float overshoot_dist;
+
+public:
+ Train(Layout &, const VehicleType &, unsigned, const std::string &);
+ ~Train();
+
+ Layout &get_layout() const { return layout; }
+ const VehicleType &get_locomotive_type() const { return loco_type; }
+ unsigned get_address() const { return address; }
+ const std::string &get_protocol() const { return protocol; }
+ void set_name(const std::string &);
+ const std::string &get_name() const { return name; }
+ void set_priority(int);
+ void yield_to(const Train &);
+ int get_priority() const { return priority; }
+ Controller &get_controller() const { return *controller; }
+
+ void add_vehicle(const VehicleType &);
+ void remove_vehicle(unsigned);
+ unsigned get_n_vehicles() const;
+ Vehicle &get_vehicle(unsigned);
+ const Vehicle &get_vehicle(unsigned) const;
+
+ void set_control(const std::string &, float);
+ void set_active(bool);
+ void set_function(unsigned, bool);
+ float get_control(const std::string &) const;
+ float get_speed() const;
+ bool is_active() const { return active; }
+ bool get_function(unsigned) const;
+ unsigned get_functions() const { return functions; }
+
+ void set_timetable(Timetable *);
+ Timetable *get_timetable() { return timetable; }
+
+ bool set_route(const Route *);
+ bool go_to(Track &);
+ bool divert(Track &);
+ const Route *get_route() const;
+ void place(Block &, unsigned);
+ void unplace();
+ bool is_placed() const { return !blocks.empty(); }
+ bool free_block(Block &);
+ void free_noncritical_blocks();
+ int get_entry_to_block(Block &) const;
+ float get_reserved_distance() const;
+
+ const std::string &get_status() const { return status; }
+
+ void tick(const Msp::Time::TimeStamp &, const Msp::Time::TimeDelta &);
+
+ void save(std::list<Msp::DataFile::Statement> &) const;
+private:
+ void control_changed(const Controller::Control &);
+ void loco_speed_event(unsigned, unsigned, bool);
+ void loco_func_event(unsigned, unsigned, bool);
+ void sensor_event(unsigned, bool);
+ void turnout_path_changed(Track &);
+ void halt_event(bool);
+ void block_reserved(const Block &, const Train *);
+ void reserve_more();
+ void check_turnout_paths(bool);
+ float get_reserved_distance_until(const Block *, bool) const;
+ float get_real_speed(unsigned) const;
+ unsigned find_speed_step(float) const;
+ float get_travel_speed() const;
+ void set_status(const std::string &);
+ void release_blocks();
+ void release_blocks(BlockList::iterator, BlockList::iterator);
+ void reverse_blocks(BlockList &) const;
+ bool advance_route(std::list<RouteRef>::iterator &, Track &);
+ Route *create_lead_route(Route *, const Route *);
+ bool is_valid_diversion(const Route &, const TrackIter &);
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <cmath>
+#include "catalogue.h"
+#include "driver.h"
+#include "layout.h"
+#include "track.h"
+#include "trackiter.h"
+#include "tracktype.h"
+#include "vehicle.h"
+#include "vehicletype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+Vehicle::Vehicle(Layout &l, const VehicleType &t):
+ layout(l),
+ type(t),
+ next(0),
+ prev(0),
+ direction(0),
+ bogie_dirs(type.get_bogies().size()),
+ front_sensor(0),
+ back_sensor(0)
+{
+ layout.add_vehicle(*this);
+}
+
+Vehicle::~Vehicle()
+{
+ if(next)
+ detach_back();
+ if(prev)
+ detach_front();
+ layout.remove_vehicle(*this);
+}
+
+void Vehicle::attach_back(Vehicle &veh)
+{
+ if(next || veh.prev)
+ throw InvalidState("Already attached");
+
+ next = &veh;
+ veh.prev = this;
+
+ if(track_pos.track)
+ propagate_backward();
+}
+
+void Vehicle::attach_front(Vehicle &veh)
+{
+ if(prev || veh.next)
+ throw InvalidState("Already attached");
+
+ prev = &veh;
+ veh.next = this;
+
+ if(prev->get_track())
+ prev->propagate_backward();
+}
+
+void Vehicle::detach_back()
+{
+ if(!next)
+ throw InvalidState("Not attached");
+
+ next->prev = 0;
+ next = 0;
+}
+
+void Vehicle::detach_front()
+{
+ if(!prev)
+ throw InvalidState("Not attached");
+
+ prev->next = 0;
+ prev = 0;
+}
+
+void Vehicle::place(Track &t, unsigned e, float o, PlaceMode m)
+{
+ track_pos = TrackPosition(&t, e, o);
+
+ if(m==FRONT_AXLE)
+ track_pos.advance(-type.get_front_axle_offset());
+ else if(m==FRONT_BUFFER)
+ track_pos.advance(-type.get_length()/2);
+ else if(m==BACK_AXLE)
+ track_pos.advance(-type.get_back_axle_offset());
+ else if(m==BACK_BUFFER)
+ track_pos.advance(type.get_length()/2);
+
+ update_position();
+ propagate_position();
+}
+
+void Vehicle::unplace()
+{
+ if(!track_pos.track)
+ return;
+
+ track_pos = TrackPosition();
+
+ if(prev)
+ prev->unplace();
+ if(next)
+ next->unplace();
+}
+
+void Vehicle::advance(float d)
+{
+ track_pos.advance(d);
+ update_position();
+ propagate_position();
+}
+
+float Vehicle::get_bogie_direction(unsigned i) const
+{
+ if(i>=bogie_dirs.size())
+ throw InvalidParameterValue("Bogie index out of range");
+ return bogie_dirs[i];
+}
+
+void Vehicle::update_position()
+{
+ TrackPoint tp;
+
+ const vector<VehicleType::Axle> &axles = type.get_axles();
+ const vector<VehicleType::Bogie> &bogies = type.get_bogies();
+ if(axles.size()>=2)
+ {
+ float wheelbase = axles.front().position-axles.back().position;
+ tp = get_point(track_pos, wheelbase, -axles.back().position/wheelbase);
+ }
+ else if(bogies.size()>=2)
+ {
+ TrackPosition front = track_pos;
+ front.advance(bogies.front().position);
+ TrackPosition back = track_pos;
+ back.advance(bogies.back().position);
+ float bogie_spacing = bogies.front().position-bogies.back().position;
+ adjust_for_distance(front, back, bogie_spacing);
+
+ const vector<VehicleType::Axle> &front_axles = bogies.front().axles;
+ float wheelbase = front_axles.front().position-front_axles.back().position;
+ TrackPoint front_point = get_point(front, wheelbase, -front_axles.back().position/wheelbase);
+
+ const vector<VehicleType::Axle> &back_axles = bogies.back().axles;
+ wheelbase = back_axles.front().position-back_axles.back().position;
+ TrackPoint back_point = get_point(back, wheelbase, -back_axles.back().position/wheelbase);
+
+ tp = get_point(front_point.pos, back_point.pos, -bogies.back().position/bogie_spacing);
+
+ bogie_dirs.front() = front_point.dir-tp.dir;
+ bogie_dirs.back() = back_point.dir-tp.dir;
+ }
+ else
+ tp = track_pos.get_point();
+
+ if(!prev)
+ check_sensor(type.get_front_axle_offset(), front_sensor);
+ if(!next)
+ check_sensor(type.get_back_axle_offset(), back_sensor);
+
+ position = tp.pos;
+ position.z += layout.get_catalogue().get_rail_elevation();
+ direction = tp.dir;
+}
+
+void Vehicle::update_position_from(const Vehicle &veh)
+{
+ int sign = (&veh==prev ? -1 : 1);
+
+ float tdist = (type.get_length()+veh.type.get_length())/2;
+ float margin = layout.get_catalogue().get_scale();
+
+ float dist = distance(veh.position, position);
+ if(dist<tdist-margin || dist>tdist+margin)
+ {
+ track_pos = veh.track_pos;
+ track_pos.advance(sign*tdist);
+ update_position();
+
+ dist = distance(veh.position, position);
+ }
+
+ track_pos.advance(sign*(tdist-dist));
+ update_position();
+}
+
+void Vehicle::propagate_position()
+{
+ if(prev)
+ propagate_forward();
+ if(next)
+ propagate_backward();
+}
+
+void Vehicle::propagate_forward()
+{
+ prev->update_position_from(*this);
+
+ if(prev->prev)
+ prev->propagate_forward();
+}
+
+void Vehicle::propagate_backward()
+{
+ next->update_position_from(*this);
+
+ if(next->next)
+ next->propagate_backward();
+}
+
+void Vehicle::check_sensor(float offset, unsigned &sensor)
+{
+ TrackPosition pos = track_pos;
+ pos.advance(offset);
+ unsigned s = pos.track->get_sensor_id();
+ if(s!=sensor)
+ {
+ /* Sensor ID under axle has changed. Deduce movement direction by using
+ the sensor ID under the midpoint of the vehicle. */
+ /* XXX This depends on the simulation running fast enough. Something
+ more robust would be preferable. */
+ unsigned old = sensor;
+ sensor = s;
+ unsigned mid = track_pos.track->get_sensor_id();
+
+ if(s && s!=mid)
+ /* There's a sensor and it's different from mid. We've just entered
+ that sensor. */
+ layout.get_driver().set_sensor(sensor, true);
+ if(old && old!=mid)
+ /* A sensor was under the axle and it was different from mid. We've
+ just left that sensor. */
+ layout.get_driver().set_sensor(old, false);
+ }
+}
+
+void Vehicle::adjust_for_distance(TrackPosition &front, TrackPosition &back, float tdist, float ratio) const
+{
+ float margin = 0.01*layout.get_catalogue().get_scale();
+ int adjust_dir = 0;
+ while(1)
+ {
+ Point front_point = front.get_point().pos;
+ Point back_point = back.get_point().pos;
+
+ float dx = front_point.x-back_point.x;
+ float dy = front_point.y-back_point.y;
+ float dz = front_point.z-back_point.z;
+ float dist = sqrt(dx*dx+dy*dy+dz*dz);
+
+ float diff = tdist-dist;
+ if(diff<-margin && adjust_dir<=0)
+ {
+ diff -= margin;
+ adjust_dir = -1;
+ }
+ else if(diff>margin && adjust_dir>=0)
+ {
+ diff += margin;
+ adjust_dir = 1;
+ }
+ else
+ return;
+
+ front.advance(diff*(1-ratio));
+ back.advance(-diff*ratio);
+ }
+}
+
+TrackPoint Vehicle::get_point(const Point &front, const Point &back, float ratio) const
+{
+ float dx = front.x-back.x;
+ float dy = front.y-back.y;
+ float dz = front.z-back.z;
+
+ TrackPoint tp;
+ tp.pos = Point(back.x+dx*ratio, back.y+dy*ratio, back.z+dz*ratio);
+ tp.dir = atan2(dy, dx);
+
+ return tp;
+}
+
+TrackPoint Vehicle::get_point(const TrackPosition &pos, float tdist, float ratio) const
+{
+ TrackPosition front = pos;
+ front.advance(tdist*(1-ratio));
+
+ TrackPosition back = pos;
+ back.advance(-tdist*ratio);
+
+ adjust_for_distance(front, back, tdist, ratio);
+ return get_point(front.get_point().pos, back.get_point().pos, ratio);
+}
+
+
+Vehicle::TrackPosition::TrackPosition():
+ track(0),
+ ep(0),
+ offs(0)
+{ }
+
+Vehicle::TrackPosition::TrackPosition(Track *t, unsigned e, float o):
+ track(t),
+ ep(e),
+ offs(o)
+{ }
+
+void Vehicle::TrackPosition::advance(float d)
+{
+ if(!track)
+ return;
+
+ offs += d;
+ TrackIter iter(track, ep);
+ while(iter)
+ {
+ float path_len = iter->get_type().get_path_length(iter->get_active_path());
+
+ if(offs>path_len)
+ {
+ offs -= path_len;
+ iter = iter.next();
+ }
+ else
+ break;
+ }
+
+ while(iter && offs<0)
+ {
+ iter = iter.flip().reverse();
+
+ if(iter)
+ {
+ float path_len = iter->get_type().get_path_length(iter->get_active_path());
+ offs += path_len;
+ }
+ }
+
+ track = iter.track();
+ ep = iter.entry();
+ if(!track)
+ offs = 0;
+}
+
+TrackPoint Vehicle::TrackPosition::get_point() const
+{
+ if(track)
+ return track->get_point(ep, offs);
+ else
+ return TrackPoint();
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_VEHICLE_H_
+#define LIBR2C2_VEHICLE_H_
+
+#include "geometry.h"
+
+namespace R2C2 {
+
+class Layout;
+class Track;
+class VehicleType;
+
+class Vehicle
+{
+public:
+ enum PlaceMode
+ {
+ CENTER,
+ FRONT_AXLE,
+ FRONT_BUFFER,
+ BACK_AXLE,
+ BACK_BUFFER
+ };
+
+private:
+ struct TrackPosition
+ {
+ Track *track;
+ unsigned ep;
+ float offs;
+
+ TrackPosition();
+ TrackPosition(Track *, unsigned, float);
+ void advance(float);
+ TrackPoint get_point() const;
+ };
+
+ Layout &layout;
+ const VehicleType &type;
+ Vehicle *next;
+ Vehicle *prev;
+ TrackPosition track_pos;
+ Point position;
+ float direction;
+ std::vector<float> bogie_dirs;
+ unsigned front_sensor;
+ unsigned back_sensor;
+
+public:
+ Vehicle(Layout &, const VehicleType &);
+ ~Vehicle();
+
+ const VehicleType &get_type() const { return type; }
+
+ void attach_back(Vehicle &);
+ void attach_front(Vehicle &);
+ void detach_back();
+ void detach_front();
+ Vehicle *get_next() const { return next; }
+ Vehicle *get_previous() const { return prev; }
+
+ void place(Track &, unsigned, float, PlaceMode = CENTER);
+ void unplace();
+ void advance(float);
+ Track *get_track() const { return track_pos.track; }
+ unsigned get_entry() const { return track_pos.ep; }
+ float get_offset() const { return track_pos.offs; }
+ const Point &get_position() const { return position; }
+ float get_direction() const { return direction; }
+ float get_bogie_direction(unsigned) const;
+private:
+ void update_position();
+ void update_position_from(const Vehicle &);
+ void propagate_position();
+ void propagate_forward();
+ void propagate_backward();
+ void check_sensor(float, unsigned &);
+
+ void adjust_for_distance(TrackPosition &, TrackPosition &, float, float = 0.5) const;
+ TrackPoint get_point(const Point &, const Point &, float = 0.5) const;
+ TrackPoint get_point(const TrackPosition &, float, float = 0.5) const;
+};
+
+} // namespace R2C2
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include "vehicletype.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+VehicleType::VehicleType(const ArticleNumber &an):
+ art_nr(an),
+ locomotive(false),
+ length(0),
+ width(0),
+ height(0)
+{ }
+
+unsigned VehicleType::get_max_function() const
+{
+ if(functions.empty())
+ return 0;
+ return (--functions.end())->first;
+}
+
+float VehicleType::get_front_axle_offset() const
+{
+ float front = length/2;
+ if(!axles.empty())
+ front = axles.front().position;
+ if(!bogies.empty())
+ {
+ const Bogie &bogie = bogies.front();
+ front = max(front, bogie.position+bogie.axles.front().position);
+ }
+ return front;
+}
+
+float VehicleType::get_back_axle_offset() const
+{
+ float back = -length/2;
+ if(!axles.empty())
+ back = axles.back().position;
+ if(!bogies.empty())
+ {
+ const Bogie &bogie = bogies.back();
+ back = min(back, bogie.position+bogie.axles.back().position);
+ }
+ return back;
+}
+
+
+VehicleType::Axle::Axle():
+ position(0),
+ wheel_dia(0),
+ powered(false)
+{ }
+
+
+VehicleType::Bogie::Bogie():
+ position(0),
+ rotate_object(false)
+{ }
+
+
+VehicleType::Loader::Loader(VehicleType &vt):
+ DataFile::ObjectLoader<VehicleType>(vt)
+{
+ add("axle", &Loader::axle);
+ add("bogie", &Loader::bogie);
+ add("function", &Loader::function);
+ add("height", &Loader::height);
+ add("length", &Loader::length);
+ add("locomotive", &VehicleType::locomotive);
+ add("object", &VehicleType::object);
+ add("name", &VehicleType::name);
+ add("width", &Loader::width);
+}
+
+void VehicleType::Loader::axle()
+{
+ Axle axl;
+ load_sub(axl);
+ obj.axles.push_back(axl);
+}
+
+void VehicleType::Loader::bogie()
+{
+ Bogie bog;
+ load_sub(bog);
+ obj.bogies.push_back(bog);
+}
+
+void VehicleType::Loader::function(unsigned i, const string &f)
+{
+ obj.functions[i] = f;
+}
+
+void VehicleType::Loader::height(float h)
+{
+ obj.height = h/1000;
+}
+
+void VehicleType::Loader::length(float l)
+{
+ obj.length = l/1000;
+}
+
+void VehicleType::Loader::width(float w)
+{
+ obj.width = w/1000;
+}
+
+
+VehicleType::Axle::Loader::Loader(Axle &a):
+ DataFile::ObjectLoader<Axle>(a)
+{
+ add("object", &Axle::object);
+ add("position", &Loader::position);
+ add("powered", &Axle::powered);
+ add("wheel_diameter", &Loader::wheel_diameter);
+}
+
+void VehicleType::Axle::Loader::position(float p)
+{
+ obj.position = p/1000;
+}
+
+void VehicleType::Axle::Loader::wheel_diameter(float d)
+{
+ obj.wheel_dia = d/1000;
+}
+
+
+VehicleType::Bogie::Loader::Loader(Bogie &b):
+ DataFile::ObjectLoader<Bogie>(b)
+{
+ add("axle", &Loader::axle);
+ add("object", &Bogie::object);
+ add("position", &Loader::position);
+ add("rotate_object", &Bogie::rotate_object);
+}
+
+void VehicleType::Bogie::Loader::axle()
+{
+ Axle axl;
+ load_sub(axl);
+ obj.axles.push_back(axl);
+}
+
+void VehicleType::Bogie::Loader::position(float p)
+{
+ obj.position = p/1000;
+}
+
+} // namespace R2C2
--- /dev/null
+/* $Id$
+
+This file is part of R²C²
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBR2C2_VEHICLETYPE_H_
+#define LIBR2C2_VEHICLETYPE_H_
+
+#include <msp/datafile/objectloader.h>
+#include "articlenumber.h"
+
+namespace R2C2 {
+
+class VehicleType
+{
+public:
+ class Loader: public Msp::DataFile::ObjectLoader<VehicleType>
+ {
+ public:
+ Loader(VehicleType &);
+ private:
+ void axle();
+ void bogie();
+ void function(unsigned, const std::string &);
+ void height(float);
+ void length(float);
+ void width(float);
+ };
+
+ struct Axle
+ {
+ class Loader: public Msp::DataFile::ObjectLoader<Axle>
+ {
+ public:
+ Loader(Axle &);
+ private:
+ void position(float);
+ void wheel_diameter(float);
+ };
+
+ float position;
+ float wheel_dia;
+ bool powered;
+ std::string object;
+
+ Axle();
+ };
+
+ struct Bogie
+ {
+ class Loader: public Msp::DataFile::ObjectLoader<Bogie>
+ {
+ public:
+ Loader(Bogie &);
+ private:
+ void axle();
+ void position(float);
+ };
+
+ float position;
+ std::vector<Axle> axles;
+ std::string object;
+ bool rotate_object;
+
+ Bogie();
+ };
+
+private:
+ ArticleNumber art_nr;
+ std::string name;
+ bool locomotive;
+ std::map<unsigned, std::string> functions;
+ float length;
+ float width;
+ float height;
+ std::vector<Axle> axles;
+ std::vector<Bogie> bogies;
+ std::string object;
+
+public:
+ VehicleType(const ArticleNumber &);
+
+ const ArticleNumber &get_article_number() const { return art_nr; }
+ const std::string &get_name() const { return name; }
+ bool is_locomotive() const { return locomotive; }
+ unsigned get_max_function() const;
+ const std::map<unsigned, std::string> &get_functions() const { return functions; }
+ float get_length() const { return length; }
+ float get_width() const { return width; }
+ float get_height() const { return height; }
+ const std::vector<Axle> &get_axles() const { return axles; }
+ const std::vector<Bogie> &get_bogies() const { return bogies; }
+ float get_front_axle_offset() const;
+ float get_back_axle_offset() const;
+ const std::string &get_object() const { return object; }
+};
+
+} // namespace R2C2
+
+#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
using namespace Msp;
-namespace Marklin {
+namespace R2C2 {
Client::Client(const Catalogue &c):
catalogue(c),
signal_error.emit(pkt.message);
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLINNET_CLIENT_H_
-#define MARKLINNET_CLIENT_H_
+#ifndef R2C2_NET_CLIENT_H_
+#define R2C2_NET_CLIENT_H_
#include <msp/io/eventdispatcher.h>
#include <msp/net/communicator.h>
-#include "libmarklin/catalogue.h"
+#include "libr2c2/catalogue.h"
#include "packets.h"
#include "protocol.h"
#include "train.h"
-namespace Marklin {
+namespace R2C2 {
class Client: public Msp::Net::PacketReceiver<TrainInfoPacket>,
Msp::Net::PacketReceiver<TrainFunctionPacket>,
virtual void receive(const ErrorPacket &);
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLINNET_PACKETS_H_
-#define MARKLINNET_PACKETS_H_
+#ifndef R2C2_NET_PACKETS_H_
+#define R2C2_NET_PACKETS_H_
#include <string>
-namespace Marklin {
+namespace R2C2 {
struct TrainInfoPacket
{
std::string message;
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include "packets.h"
#include "protocol.h"
-namespace Marklin {
+namespace R2C2 {
Protocol::Protocol()
{
add<ErrorPacket>() (&ErrorPacket::message);
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLINNET_PROTOCOL_H_
-#define MARKLINNET_PROTOCOL_H_
+#ifndef R2C2_NET_PROTOCOL_H_
+#define R2C2_NET_PROTOCOL_H_
#include <msp/net/protocol.h>
-namespace Marklin {
+namespace R2C2 {
class Protocol: public Msp::Net::Protocol
{
Protocol();
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/net/inet.h>
-#include "libmarklin/route.h"
-#include "libmarklin/train.h"
-#include "libmarklin/vehicletype.h"
+#include "libr2c2/route.h"
+#include "libr2c2/train.h"
+#include "libr2c2/vehicletype.h"
#include "server.h"
using namespace std;
using namespace Msp;
-namespace Marklin {
+namespace R2C2 {
Server::Server(Layout &l):
layout(l),
comm.send(pkt);
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLINNET_SERVER_H_
-#define MARKLINNET_SERVER_H_
+#ifndef R2C2_NET_SERVER_H_
+#define R2C2_NET_SERVER_H_
#include <msp/io/eventdispatcher.h>
#include <msp/net/communicator.h>
#include <msp/net/streamsocket.h>
#include <msp/net/streamlistensocket.h>
-#include "libmarklin/layout.h"
+#include "libr2c2/layout.h"
#include "packets.h"
#include "protocol.h"
-namespace Marklin {
+namespace R2C2 {
class Server
{
void send(const P &);
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
-namespace Marklin {
+namespace R2C2 {
NetTrain::NetTrain(Client &c, const TrainInfoPacket &pkt):
client(c),
signal_route_changed.emit(route);
}
-} // namespace Marklin
+} // namespace R2C2
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLINNET_TRAIN_H_
-#define MARKLINNET_TRAIN_H_
+#ifndef R2C2_NET_TRAIN_H_
+#define R2C2_NET_TRAIN_H_
#include <sigc++/signal.h>
-#include "libmarklin/vehicletype.h"
+#include "libr2c2/vehicletype.h"
#include "packets.h"
-namespace Marklin {
+namespace R2C2 {
class Client;
void process_packet(const TrainStatusPacket &);
};
-} // namespace Marklin
+} // namespace R2C2
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
gtk.iteration(false);
}
-void Remote::train_added(Marklin::NetTrain &t)
+void Remote::train_added(R2C2::NetTrain &t)
{
TrainPanel *panel = new TrainPanel(*this, client, t);
if(!train_panels.empty())
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
{
private:
Msp::IO::EventDispatcher event_disp;
- Marklin::Catalogue catalogue;
- Marklin::Client client;
+ R2C2::Catalogue catalogue;
+ R2C2::Client client;
Gtk::Main gtk;
Gtk::Window window;
Gtk::Box *train_box;
public:
Remote(int argc, char **argv);
- const Marklin::Catalogue &get_catalogue() const { return catalogue; }
+ const R2C2::Catalogue &get_catalogue() const { return catalogue; }
private:
void tick();
- void train_added(Marklin::NetTrain &);
+ void train_added(R2C2::NetTrain &);
};
#endif
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
-TrainPanel::TrainPanel(Remote &r, Marklin::Client &c, Marklin::NetTrain &t):
+TrainPanel::TrainPanel(Remote &r, R2C2::Client &c, R2C2::NetTrain &t):
remote(r),
client(c),
train(t)
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
};
Remote &remote;
- Marklin::Client &client;
- Marklin::NetTrain &train;
+ R2C2::Client &client;
+ R2C2::NetTrain &train;
Gtk::Scale *scl_speed;
Gtk::Label *lbl_status;
Gtk::CheckButton *chk_reverse;
std::map<unsigned, Gtk::CheckButton *> chk_funcs;
public:
- TrainPanel(Remote &, Marklin::Client &, Marklin::NetTrain &);
+ TrainPanel(Remote &, R2C2::Client &, R2C2::NetTrain &);
private:
void name_changed(const std::string &);
void status_changed(const std::string &);
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
using namespace std;
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
Application::RegApp<Serial> Serial::reg;
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
{
private:
Msp::IO::EventDispatcher event_disp;
- Marklin::Catalogue catalogue;
- Marklin::Client client;
+ R2C2::Catalogue catalogue;
+ R2C2::Client client;
Msp::IO::Serial serial_port;
- Marklin::NetTrain *train;
+ R2C2::NetTrain *train;
bool reverse;
char rx_buf[3];
unsigned rx_fill;
private:
virtual void tick();
- void train_added(Marklin::NetTrain &);
+ void train_added(R2C2::NetTrain &);
void error(const std::string &);
- void set_train(Marklin::NetTrain *);
+ void set_train(R2C2::NetTrain *);
void next_train();
void prev_train();
void data_available();
/* $Id$
-This file is part of the MSP Märklin suite
+This file is part of R²C²
Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/datafile/loader.h>
#include <msp/datafile/parser.h>
#include <msp/io/print.h>
-#include "libmarklin/articlenumber.h"
+#include "libr2c2/articlenumber.h"
using namespace std;
using namespace Msp;
-using namespace Marklin;
+using namespace R2C2;
class ShoppingList: public Application
{