Initialisation depot
@@ -0,0 +1,198 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER
|
||||
// COPYRIGHT (c) 2013-2015 Gregg E. Berman
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DCC++ CONTROLLER is a Java program written using the 64-bit Processing Library
|
||||
// and Processing IDE (version 3.01).
|
||||
//
|
||||
// DCC++ CONTROLLER provides users with a fully customizeable graphical
|
||||
// front end for the total control of model trains and model train layouts
|
||||
// via its companion program, DCC++ BASE STATION.
|
||||
//
|
||||
// DCC++ BASE STATION allows a standard Arduino Uno with an Arduino Motor Shield
|
||||
// to be used as a fully-functioning digital command and control (DCC) base station
|
||||
// for controlling model train layouts that conform to current National Model
|
||||
// Railroad Association (NMRA) DCC standards.
|
||||
//
|
||||
// DCC++ CONTROLLER communicates with DCC++ BASE STATION using simple text commands sent
|
||||
// via a standard USB Serial Cord at speeds of up to 115200 Baud. A Bluetooth Wireless
|
||||
// Connection may be used in place of a USB Serial Cord without any software modification.
|
||||
//
|
||||
// This version of DCC++ CONTROLLER supports:
|
||||
//
|
||||
// * Multi-Cab / Multi-Throttle configurations using 128-step speed control
|
||||
// * 2-byte and 4-byte cab addresses
|
||||
// * Customizable cab function buttons F0-F12
|
||||
// * User-created multi-layout track plan
|
||||
// * Customizeable turnouts and crossovers with controls integrated into track plan
|
||||
// * Customizeable routes with configurable buttons
|
||||
// * Customizeable routes with route buttons integrated into track plan
|
||||
// * Master Power Button
|
||||
// * Customizable key-controls
|
||||
// * Real-time current monitor
|
||||
// * Optional track-integrated sensors
|
||||
// * Optional user-created Auto Pilot routines (when used with track-integrated sensors)
|
||||
// * Manual activation/de-activation of accessory functions using 512 addresses, each with 4 sub-addresses
|
||||
// * Programming on the Main Operations Track
|
||||
// - write configuration variable bytes
|
||||
// - set/clear specific configuration variable bits
|
||||
// * Programming on the Programming Track
|
||||
// - write configuration variable bytes
|
||||
// - read configuration variable bytes
|
||||
//
|
||||
// With the exception of a standard 15V power supply for the Arduino Uno that can
|
||||
// be purchased in any electronics store, no additional hardware is required.
|
||||
//
|
||||
// Neither DCC++ BASE STATION nor DCC++ CONTROLLER use any known proprietary or
|
||||
// commercial hardware, software, interfaces, specifications, or methods related
|
||||
// to the control of model trains using NMRA DCC standards. Both programs are wholly
|
||||
// original, developed by the author, and are not derived from any known commercial,
|
||||
// free, or open-source model railroad control packages by any other parties.
|
||||
//
|
||||
// However, DCC++ BASE STATION and DCC++ CONTROLLER do heavily rely on the IDEs and
|
||||
// embedded libraries associated with Arduino and Processing. Tremendous thanks to those
|
||||
// responsible for these terrific open-source initiatives that enable programs like
|
||||
// DCC++ to be developed and distributed in the same fashion.
|
||||
//
|
||||
// REFERENCES:
|
||||
//
|
||||
// NMRA DCC Standards: http://www.nmra.org/standards/DCC/standards_rps/DCCStds.html
|
||||
// Arduino: http://www.arduino.cc/
|
||||
// Processing: http://processing.org/
|
||||
// GNU General Public License: http://opensource.org/licenses/GPL-3.0
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import processing.serial.*;
|
||||
import processing.net.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.*;
|
||||
|
||||
final String CONTROLLER_VERSION = "3.0";
|
||||
final int BASE_BAUD = 115200;
|
||||
final int SCREEN_WIDTH = 1366;
|
||||
final int SCREEN_HEIGHT = 768;
|
||||
final String STATUS_FILE = "dccStatus.xml";
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void settings(){
|
||||
size(SCREEN_WIDTH,SCREEN_HEIGHT);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setup(){
|
||||
Initialize();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void draw(){
|
||||
|
||||
background(backgroundColor);
|
||||
|
||||
for(DccComponent dcc : dccComponents)
|
||||
dcc.display();
|
||||
|
||||
if(frameCount==1) // if this is the first frame, just display components and return (otherwise user stare at a blank screen while serial is opening
|
||||
return;
|
||||
|
||||
if(frameCount==2) // is this is the second frame, open the serial port --- screen will have already been displayed in prior frame
|
||||
aPort.open(arduinoPortXML.getContent());
|
||||
|
||||
for(int i=buttonQueue2.size()-1;i>=0;i--){
|
||||
buttonQueue2.get(i).init();
|
||||
buttonQueue2.remove(i);
|
||||
}
|
||||
|
||||
for(int i=buttonQueue.size()-1;i>=0;i--){
|
||||
buttonQueue2.add(buttonQueue.get(i));;
|
||||
buttonQueue.remove(i);
|
||||
}
|
||||
|
||||
if(!mousePressed){
|
||||
cursorType=ARROW;
|
||||
previousComponent=selectedComponent;
|
||||
selectedComponent=null;
|
||||
|
||||
int nComponents = dccComponents.size();
|
||||
|
||||
for(int i=nComponents-1;i>=0;i--)
|
||||
dccComponents.get(i).check();
|
||||
|
||||
cursor(cursorType);
|
||||
}
|
||||
|
||||
int m=millis();
|
||||
if(m-lastTime>250 && aPort!=null && currentMeter.isOn){
|
||||
lastTime=m;
|
||||
aPort.write("<c>");
|
||||
}
|
||||
|
||||
msgBoxClock.setMessage(nf(hour(),2)+":"+nf(minute(),2)+":"+nf(second(),2));
|
||||
|
||||
if(saveXMLFlag){
|
||||
try{
|
||||
saveXML(dccStatusXML,STATUS_FILE);
|
||||
saveXMLFlag=false;
|
||||
} catch(Exception e){
|
||||
println("Couldn't save. Will retry");
|
||||
}
|
||||
}
|
||||
|
||||
autoPilot.safetyCheck();
|
||||
|
||||
} // draw
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
abstract class DccComponent{
|
||||
Window window=null;
|
||||
int xPos, yPos;
|
||||
String componentName="NAME NOT DEFINED";
|
||||
abstract void display();
|
||||
void check(){};
|
||||
void pressed(){};
|
||||
void rightClick(){};
|
||||
void shiftPressed(){};
|
||||
void released(){};
|
||||
void drag(){};
|
||||
void init(){};
|
||||
|
||||
protected int xWindow(){
|
||||
if(window==null)
|
||||
return 0;
|
||||
return window.xPos;
|
||||
}
|
||||
|
||||
protected int yWindow(){
|
||||
if(window==null)
|
||||
return 0;
|
||||
return window.yPos;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
interface CallBack{
|
||||
void execute(int n, String c);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,491 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Core Components
|
||||
//
|
||||
// PowerButton - send power on/off command to the DCC++ Base Station
|
||||
//
|
||||
// CurrentMeter - monitors main track current draw from the DCC++ Base Station
|
||||
// - displays scrolling bar chart of current measured
|
||||
//
|
||||
// HelpButton - toggles Help Window
|
||||
//
|
||||
// QuitButton - quits DCC++ Controller
|
||||
// - connection to DCC++ Base Station terminated
|
||||
// - NOTE: track power remains on and trains will continue to operate
|
||||
// since DCC+ Base Station operates independently!
|
||||
//
|
||||
// AccessoryButton - sends a DCC ACCESSORY COMMAND to the DCC++ Base Station
|
||||
// to either activate or de-activate an accessory depending on
|
||||
// whether the button is labeled "ON" or "OFF"
|
||||
// - two pre-specified input boxes are used: one for the user
|
||||
// to input the desired accessory address, and one for
|
||||
// accessory number (sub-address)
|
||||
// - the default configuration of DCC++ Controller defines an
|
||||
// Accessory Window that includes these two input boxes as well
|
||||
// as ON and OFF buttons.
|
||||
//
|
||||
// CleaningCarButton - sends a DCC THROTTLE COMMAND to the DCC++ Base Station that operates
|
||||
// a mobile decoder with a pre-specified cab number
|
||||
// - this decoder drives a motor that spins a cleaning pad in a
|
||||
// track-cleaning car
|
||||
// - clicking the button toggles the throttle between either 0 or 126 (max speed)
|
||||
// - the default configuration of DCC++ Controller defines an
|
||||
// Extras Window that includes this button
|
||||
//
|
||||
// LEDColorButton - provide for interactive control of an LED-RGB Light Strip
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: PowerButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class PowerButton extends RectButton{
|
||||
|
||||
PowerButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText);
|
||||
}
|
||||
|
||||
PowerButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, bText, ButtonType.NORMAL);
|
||||
} // PowerButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOn(){
|
||||
aPort.write("<1>");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void shiftPressed(){
|
||||
aPort.write("<Z 1 0>");
|
||||
exit();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOff(){
|
||||
aPort.write("<0>");
|
||||
}
|
||||
|
||||
} // PowerButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: CurrentMeter
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CurrentMeter extends DccComponent{
|
||||
int nSamples, kHeight;
|
||||
int maxCurrent;
|
||||
int[] samples;
|
||||
int sampleIndex;
|
||||
int nGridLines;
|
||||
boolean isOn;
|
||||
|
||||
CurrentMeter(int xPos, int yPos, int nSamples, int kHeight, int maxCurrent, int nGridLines){
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.nSamples=nSamples;
|
||||
this.kHeight=kHeight;
|
||||
this.maxCurrent=maxCurrent;
|
||||
this.nGridLines=nGridLines;
|
||||
this.isOn=true;
|
||||
samples=new int[nSamples];
|
||||
sampleIndex=nSamples-1;
|
||||
dccComponents.add(this);
|
||||
} // CurrentMeter
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
int i;
|
||||
rectMode(CORNER);
|
||||
noFill();
|
||||
strokeWeight(1);
|
||||
textFont(buttonFont,8);
|
||||
textAlign(LEFT,CENTER);
|
||||
stroke(200);
|
||||
rect(xPos,yPos,nSamples+1,kHeight+2);
|
||||
if(isOn)
|
||||
stroke(50,200,100);
|
||||
else
|
||||
stroke(200,100,100);
|
||||
for(i=0;i<nSamples;i++){
|
||||
line(xPos+1+i,yPos+kHeight+1,xPos+1+i,yPos+kHeight+1-samples[(sampleIndex+i)%nSamples]*kHeight/maxCurrent);
|
||||
}
|
||||
stroke(200);
|
||||
for(i=1;i<nGridLines;i++){
|
||||
line(xPos+1,yPos+kHeight+1-kHeight*i/nGridLines,xPos+1+nSamples,yPos+kHeight+1-kHeight*i/nGridLines);
|
||||
}
|
||||
fill(255);
|
||||
for(i=0;i<=nGridLines;i++){
|
||||
text(nf(i*2000/nGridLines,0)+" mA",xPos+10+nSamples,yPos+kHeight+1-kHeight*i/nGridLines);
|
||||
}
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void addSample(int s){
|
||||
|
||||
samples[sampleIndex]=s;
|
||||
sampleIndex=(sampleIndex+1)%nSamples;
|
||||
}
|
||||
|
||||
} // CurrentMeter Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: AccessoryButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AccessoryButton extends EllipseButton{
|
||||
InputBox accAddInput, accSubAddInput;
|
||||
|
||||
AccessoryButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox accAddInput, InputBox accSubAddInput){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, accAddInput, accSubAddInput);
|
||||
}
|
||||
|
||||
AccessoryButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox accAddInput, InputBox accSubAddInput){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT);
|
||||
this.accAddInput=accAddInput;
|
||||
this.accSubAddInput=accSubAddInput;
|
||||
} // AccessoryButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
super.pressed();
|
||||
int accAddress=accAddInput.getIntValue();
|
||||
int accSubAddress=accSubAddInput.getIntValue();
|
||||
if(accAddress>511)
|
||||
msgBoxMain.setMessage("Error - Accessory Address must be in range 0-511",color(255,30,30));
|
||||
else if(accSubAddress>3)
|
||||
msgBoxMain.setMessage("Error - Accessory Sub Address must be in range 0-3",color(255,30,30));
|
||||
else
|
||||
aPort.write("<a"+accAddress+" "+accSubAddress+" "+(bText.equals("ON")?1:0)+">");
|
||||
}
|
||||
|
||||
} // AccessoryButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: Quit Button
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class QuitButton extends RectButton{
|
||||
|
||||
QuitButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText);
|
||||
}
|
||||
|
||||
QuitButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.NORMAL);
|
||||
} // PowerButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOn(){
|
||||
super.turnOn();
|
||||
exit();
|
||||
}
|
||||
|
||||
} // QuitButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: Help Button
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class HelpButton extends EllipseButton{
|
||||
|
||||
HelpButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText);
|
||||
}
|
||||
|
||||
HelpButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT);
|
||||
} // PowerButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
super.pressed();
|
||||
helpWindow.toggle();
|
||||
}
|
||||
|
||||
} // HelpButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: CleaningCar Button
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CleaningCarButton extends RectButton{
|
||||
int cab;
|
||||
int reg;
|
||||
|
||||
CleaningCarButton(int cab, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
this(null, cab, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText);
|
||||
}
|
||||
|
||||
CleaningCarButton(Window window, int cab, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, bText, ButtonType.NORMAL);
|
||||
reg=cabButtons.size()+1;
|
||||
this.cab=cab;
|
||||
} // PowerButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOn(){
|
||||
super.turnOn();
|
||||
aPort.write("<t"+reg+" "+cab+" 126 1>");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOff(){
|
||||
super.turnOff();
|
||||
aPort.write("<t"+reg+" "+cab+" 0 1>");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void shiftPressed(){
|
||||
autoPilot.clean();
|
||||
}
|
||||
|
||||
} // CleaningCarButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: LED Color Button
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class LEDColorButton extends DccComponent{
|
||||
|
||||
int bWidth, bHeight;
|
||||
float hue;
|
||||
float sat;
|
||||
float val;
|
||||
|
||||
LEDColorButton(Window window, int xPos, int yPos, int bWidth, int bHeight, float hue, float sat, float val){
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.bWidth=bWidth;
|
||||
this.bHeight=bHeight;
|
||||
this.hue=hue;
|
||||
this.sat=sat;
|
||||
this.val=val;
|
||||
this.window=window;
|
||||
window.windowComponents.add(this);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
rectMode(CENTER);
|
||||
colorMode(HSB,1.0,1.0,1.0);
|
||||
fill(hue,sat,val);
|
||||
rect(xPos+xWindow(),yPos+yWindow(),bWidth,bHeight);
|
||||
colorMode(RGB,255);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void update(int s){
|
||||
color c;
|
||||
colorMode(HSB,1.0,1.0,1.0);
|
||||
c=color(hue,sat,val);
|
||||
colorMode(RGB,255);
|
||||
aPort.write("<G RGB "+int(red(c))+" "+int(green(c))+" "+int(blue(c))+" "+s+">");
|
||||
ledHueMsg.setMessage("Hue: "+int(hue*360),color(200,200,200));
|
||||
ledSatMsg.setMessage("Sat: "+int(sat*100),color(200,200,200));
|
||||
ledValMsg.setMessage("Val: "+int(val*100),color(200,200,200));
|
||||
ledRedMsg.setMessage("Red: "+int(red(c)),color(200,200,200));
|
||||
ledGreenMsg.setMessage("Green: "+int(green(c)),color(200,200,200));
|
||||
ledBlueMsg.setMessage("Blue: "+int(blue(c)),color(200,200,200));
|
||||
}
|
||||
|
||||
} // LEDColorButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: LED Value Selector
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class LEDValSelector extends DccComponent{
|
||||
|
||||
int bWidth, bHeight;
|
||||
LEDColorButton cButton;
|
||||
PImage valBox;
|
||||
|
||||
LEDValSelector(Window window, int xPos, int yPos, int bWidth, int bHeight, LEDColorButton cButton){
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.bWidth=bWidth;
|
||||
this.bHeight=bHeight;
|
||||
this.cButton=cButton;
|
||||
valBox = createImage(bWidth+1,bHeight+1,RGB);
|
||||
this.window=window;
|
||||
window.windowComponents.add(this);
|
||||
|
||||
colorMode(HSB,1.0,1.0,1.0);
|
||||
valBox.loadPixels();
|
||||
|
||||
for(int y=0;y<valBox.height;y++){
|
||||
for(int x=0;x<valBox.width;x++){
|
||||
valBox.pixels[x+y*valBox.width]=color(0,0,float(x)/float(bWidth)); // since x will be maximum at width of box, normalize by bWidth which is one less than box width to ensure max brightness is 1.0
|
||||
}
|
||||
}
|
||||
|
||||
valBox.updatePixels();
|
||||
colorMode(RGB,255);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
|
||||
imageMode(CORNER);
|
||||
colorMode(HSB,1.0,1.0,1.0);
|
||||
tint(cButton.hue,cButton.sat,1.0);
|
||||
image(valBox,xPos+xWindow(),yPos+yWindow());
|
||||
noTint();
|
||||
fill(0.0,0.0,1.0);
|
||||
noStroke();
|
||||
pushMatrix();
|
||||
translate(xPos+xWindow()+cButton.val*float(bWidth),yPos+yWindow()-2);
|
||||
triangle(0,0,-5,-10,5,-10);
|
||||
translate(0,bHeight+4);
|
||||
triangle(0,0,-5,10,5,10);
|
||||
rectMode(CORNER);
|
||||
rect(-5,10,10,10);
|
||||
fill(0,0,0);
|
||||
triangle(0,15,-5,20,5,20);
|
||||
popMatrix();
|
||||
colorMode(RGB,255);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
|
||||
if(selectedComponent==null && mouseX>=xPos+xWindow()+cButton.val*float(bWidth)-5 && mouseX<=xPos+xWindow()+cButton.val*float(bWidth)+5 && mouseY>=yPos+yWindow()+bHeight+2 && mouseY<=yPos+yWindow()+bHeight+22){
|
||||
cursorType=HAND;
|
||||
selectedComponent=this;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void drag(){
|
||||
cButton.val=constrain(float(mouseX-xPos-xWindow())/bWidth,0.0,1.0);
|
||||
cButton.update(0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void released(){
|
||||
cButton.update(1);
|
||||
}
|
||||
|
||||
} // LEDValSelector Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: LED Color Selector
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class LEDColorSelector extends DccComponent{
|
||||
|
||||
PImage colorWheel;
|
||||
int radius;
|
||||
LEDColorButton cButton;
|
||||
|
||||
LEDColorSelector(Window window, int xPos, int yPos, int radius, LEDColorButton cButton){
|
||||
float d, h;
|
||||
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.radius=radius;
|
||||
this.cButton=cButton;
|
||||
colorWheel=createImage(radius*2+1,radius*2+1,RGB);
|
||||
this.window=window;
|
||||
window.windowComponents.add(this);
|
||||
|
||||
colorWheel.loadPixels();
|
||||
colorMode(HSB,1.0,1.0,1.0);
|
||||
|
||||
for(int i=0, y=radius;y>=-radius;y--){
|
||||
for(int x=-radius;x<=radius;x++){
|
||||
d=sqrt(x*x+y*y);
|
||||
if(d<0.5){
|
||||
colorWheel.pixels[i]=color(0.0,0.0,1.0); // center of wheel always has zero saturation (hue does not matter)
|
||||
} else
|
||||
if(d>radius){
|
||||
colorWheel.pixels[i]=color(0.0,0.0,0.0); // outside of wheel is always fully black (hue and saturation does not matter)
|
||||
} else {
|
||||
h=acos(float(x)/d); // find angle in radians
|
||||
if(y<0) // adjust angle to reflect lower half of wheel
|
||||
h=TWO_PI-h;
|
||||
colorWheel.pixels[i]=color(h/TWO_PI,d/float(radius),1.0); // hue is based on angle normalized to 1.0, saturation is based on distance to center normalized to 1.0, brightness is always 1.0
|
||||
}
|
||||
i++;
|
||||
} // x-loop
|
||||
} // y-loop
|
||||
|
||||
colorMode(RGB,255);
|
||||
colorWheel.updatePixels();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
imageMode(CENTER);
|
||||
colorMode(HSB,1.0,1.0,1.0);
|
||||
image(colorWheel,xPos+xWindow(),yPos+yWindow());
|
||||
colorMode(RGB,255);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
|
||||
if(selectedComponent==null && ((pow(mouseX-xPos-xWindow(),2)+pow(mouseY-yPos-yWindow(),2))<=pow(radius,2))){
|
||||
cursorType=CROSS;
|
||||
selectedComponent=this;
|
||||
}
|
||||
|
||||
} // check
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
drag();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void drag(){
|
||||
float d,h;
|
||||
color selectedColor;
|
||||
|
||||
d=sqrt(pow(mouseX-xPos-xWindow(),2)+pow(mouseY-yPos-yWindow(),2));
|
||||
if(d<0.5){
|
||||
h=0.0;
|
||||
} else {
|
||||
h=acos(float(mouseX-xPos-xWindow())/d);
|
||||
if(mouseY>(yPos+yWindow()))
|
||||
h=TWO_PI-h;
|
||||
cButton.hue=h/TWO_PI;
|
||||
cButton.sat=constrain(d/float(radius),0.0,1.0);
|
||||
}
|
||||
|
||||
cButton.update(0);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void released(){
|
||||
cButton.update(1);
|
||||
}
|
||||
|
||||
} // LEDColorSelector Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Constants
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum ButtonType{
|
||||
NORMAL,
|
||||
ONESHOT,
|
||||
HOLD,
|
||||
REVERSE,
|
||||
T_COMMAND,
|
||||
TI_COMMAND,
|
||||
Z_COMMAND
|
||||
}
|
||||
|
||||
enum InputType{
|
||||
BIN ("[01]"),
|
||||
DEC ("[0-9]"),
|
||||
HEX ("[A-Fa-f0-9]");
|
||||
|
||||
final String regexp;
|
||||
InputType(String regexp){
|
||||
this.regexp=regexp;
|
||||
}
|
||||
}
|
||||
|
||||
enum CabFunction{
|
||||
F_LIGHT,
|
||||
R_LIGHT,
|
||||
D_LIGHT,
|
||||
BELL,
|
||||
HORN,
|
||||
S_HORN
|
||||
}
|
||||
|
||||
enum ThrottleSpeed{
|
||||
FULL,
|
||||
SLOW,
|
||||
STOP,
|
||||
REVERSE,
|
||||
REVERSE_SLOW;
|
||||
|
||||
static ThrottleSpeed index(String findName){
|
||||
for(ThrottleSpeed p : ThrottleSpeed.values()){
|
||||
if(p.name().equals(findName))
|
||||
return(p);
|
||||
}
|
||||
return(null);
|
||||
}
|
||||
}
|
||||
|
||||
enum AutoProgram{
|
||||
NONE ("NONE"),
|
||||
ALL_CABS_RUN ("ALL CABS RUN"),
|
||||
ALL_CABS_PARK ("ALL CABS PARK"),
|
||||
SINGLE_CAB_PARK ("SINGLE CAB PARK"),
|
||||
AUTO_CLEAN ("AUTO CLEAN"),
|
||||
SINGLE_CAB_RUN ("SINGLE CAB RUN");
|
||||
|
||||
String name;
|
||||
AutoProgram(String name){
|
||||
this.name=name;
|
||||
}
|
||||
static AutoProgram index(String findName){
|
||||
for(AutoProgram p : AutoProgram.values()){
|
||||
if(p.name.equals(findName))
|
||||
return(p);
|
||||
}
|
||||
return(null);
|
||||
}
|
||||
|
||||
boolean equals(AutoProgram p){
|
||||
return(this==p);
|
||||
}
|
||||
|
||||
}
|
||||
528
DCC-Centrale/Firmware/Controller/DCCpp_Controller/dCabs.pde
Normal file
@@ -0,0 +1,528 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Classes for Cab Throttle and Cab Function Controls
|
||||
//
|
||||
// Throttle - creates a sliding throttle to set the speed and direction
|
||||
// of one or more locomotive cabs
|
||||
// - cabs are selected by clicking any of the cab buttons
|
||||
// that have been associated with the throttle
|
||||
// - multiple throttles, each with a distinct set of cab buttons,
|
||||
// is allowed. It is also possible to define one throttle per
|
||||
// cab, in which case a visible cab button would not be needed
|
||||
// since there is nothing to select
|
||||
// - moving the slider up or down sends a DCC THROTTLE COMMAND to
|
||||
// the DCC++ Base Station with the cab addres and register number
|
||||
// specified by the selected can button
|
||||
// - throttle commands assume mobile decoders are configured for 128-step speed control
|
||||
// with speeds ranging from a minimum of 0 to a maximum of 126.
|
||||
// - the throttle command sent to the DCC++ Base Station also codes whether motion
|
||||
// is forward or backward
|
||||
// - negative throttle numbers are NOT used to indicate reverse motion
|
||||
// - a negative throttle number is used to instruct the DCC++ Base Station
|
||||
// to initiate an immediate emergency stop of the specified cab.
|
||||
// - this is in contrast to setting the throttle to 0, in which case the
|
||||
// cab will stop according to any deceleration parameters (which may allow the locomotive
|
||||
// to coast before stopping)
|
||||
// - throttle slider can also be controlled with arrows as follows:
|
||||
//
|
||||
// * UP ARROW = increase speed by one unit in the forward direction
|
||||
// (which decreases speed if already moving in the reverse direction)
|
||||
// * DOWN ARROW = increase speed by one unit in the reverse direction
|
||||
// (which decreases speed if already moving in the forward direction)
|
||||
// * LEFT ARROW = set speed to zero (locomotive will coast to stop if configured with deceleration)
|
||||
// * RIGHT ARROW = emergency stop (locomotive will stop immediately, ignoring any deceleration parameters)
|
||||
//
|
||||
// - Note: throttle slider and arrow buttons will not permit single action that causes locomotive
|
||||
// to stop and then reverse. This allows users to move slider or press arrow keys to slow
|
||||
// locomotive to zero without worrying about overshooting and reversing direction. Once slider is
|
||||
// at zero, reclick to start sliding in reverse direction.
|
||||
//
|
||||
// CabButton - defines a button to activate a given cab address for a given throttle
|
||||
// - in addition to the cab address (which can be short or long), the button
|
||||
// contains:
|
||||
//
|
||||
// * informaiton on which register number the DCC++ Base Station
|
||||
// should use for throttle commands to this cab
|
||||
// * a data structure indicating which cab functions (lights, horns, etc.)
|
||||
// are defined for this cab
|
||||
//
|
||||
// FunctionButton - sends a CAB FUNCTION COMMMAND to the DCC++ Base Station to
|
||||
// activate or de-activate any cab function F0-F12
|
||||
// - function buttons are always associated with a particular throttle, but
|
||||
// are dynamically configured according to the cab selected
|
||||
// to be active for that throttle
|
||||
// - configuration information for each function button is stored in
|
||||
// a data structure contained within each cab button
|
||||
// - configuration data includes the name of each button and whether the function
|
||||
// should:
|
||||
//
|
||||
// * be toggled from on to off, or off to on, with each mouse click (e.g. a headlight)
|
||||
// * be activated upon pressing the mouse button but de-active when the mouse
|
||||
// button is released (e.g. a horn)
|
||||
// * be turned on and then immediately off with a single mouse click (e.g. coupler sounds)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: Throttle
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Throttle extends DccComponent{
|
||||
final int KMAXPOS=126;
|
||||
final int KMINPOS=-126;
|
||||
int kWidth=50;
|
||||
int kHeight=15;
|
||||
int sPos,sSpeed;
|
||||
int kMaxTemp, kMinTemp;
|
||||
float tScale;
|
||||
CabButton cabButton=null;
|
||||
|
||||
Throttle(int xPos, int yPos, float tScale){
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.tScale=tScale;
|
||||
dccComponents.add(this);
|
||||
} // Throttle
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
int i;
|
||||
|
||||
rectMode(CENTER);
|
||||
ellipseMode(CENTER);
|
||||
strokeWeight(1);
|
||||
noStroke();
|
||||
fill(255);
|
||||
rect(xPos,yPos,kWidth/2.0,(KMAXPOS-KMINPOS)*tScale);
|
||||
fill(0);
|
||||
rect(xPos,yPos,kWidth/4.0,(KMAXPOS-KMINPOS)*tScale);
|
||||
|
||||
stroke(0);
|
||||
for(i=0;i<KMAXPOS*tScale;i+=10*tScale)
|
||||
line(xPos-kWidth/4.0,yPos-i,xPos+kWidth/4.0,yPos-i);
|
||||
for(i=0;i>KMINPOS*tScale;i-=10*tScale)
|
||||
line(xPos-kWidth/4.0,yPos-i,xPos+kWidth/4.0,yPos-i);
|
||||
|
||||
if(cabButton==null)
|
||||
return;
|
||||
|
||||
noStroke();
|
||||
for(i=kWidth;i>0;i--){
|
||||
fill(230-(i*2),230-(i*2),255-(i*3));
|
||||
ellipse(xPos,yPos-cabButton.speed*tScale,i,i/2);
|
||||
}
|
||||
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
|
||||
if(cabButton==null)
|
||||
return;
|
||||
|
||||
if(selectedComponent==null && (mouseX-xPos)*(mouseX-xPos)/(kWidth*kWidth/4.0)+(mouseY-(yPos-cabButton.speed*tScale))*(mouseY-(yPos-cabButton.speed*tScale))/(kWidth*kWidth/16.0)<=1){
|
||||
cursorType=HAND;
|
||||
selectedComponent=this;
|
||||
}
|
||||
} // check
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
sPos=mouseY;
|
||||
sSpeed=cabButton.speed;
|
||||
|
||||
if(sSpeed>0){
|
||||
kMaxTemp=KMAXPOS;
|
||||
kMinTemp=0;
|
||||
}
|
||||
else if(sSpeed<0){
|
||||
kMaxTemp=0;
|
||||
kMinTemp=KMINPOS;
|
||||
}
|
||||
else{
|
||||
kMaxTemp=KMAXPOS;
|
||||
kMinTemp=KMINPOS;
|
||||
}
|
||||
|
||||
noCursor();
|
||||
} // pressed
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void drag(){
|
||||
int tPos;
|
||||
|
||||
tPos=constrain(int((sPos-mouseY)/tScale)+sSpeed,kMinTemp,kMaxTemp);
|
||||
|
||||
if(tPos>0)
|
||||
kMinTemp=0;
|
||||
else if(tPos<0)
|
||||
kMaxTemp=0;
|
||||
|
||||
cabButton.setThrottle(tPos);
|
||||
} // drag
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void keyControl(int m){
|
||||
int tPos;
|
||||
|
||||
if(m==0){ // emergency stop
|
||||
tPos=0;
|
||||
cabButton.throttleSpeed=ThrottleSpeed.STOP;
|
||||
} else {
|
||||
tPos=constrain(sSpeed+=m,kMinTemp,kMaxTemp);
|
||||
}
|
||||
|
||||
if(tPos>0)
|
||||
kMinTemp=0;
|
||||
else if(tPos<0)
|
||||
kMaxTemp=0;
|
||||
|
||||
cabButton.setThrottle(tPos);
|
||||
|
||||
} // keyControl
|
||||
|
||||
} // Throttle Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: CabButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CabButton extends RectButton{
|
||||
int reg, cab;
|
||||
int speed=0;
|
||||
String name;
|
||||
RouteButton sidingRoute;
|
||||
int sidingSensor=0;
|
||||
int parkingSensor=0;
|
||||
XML speedXML, cabDefaultXML;
|
||||
XML throttleDefaultsXML;
|
||||
ThrottleSpeed throttleSpeed=ThrottleSpeed.STOP;
|
||||
Window fbWindow;
|
||||
ArrayList<Window> windowList = new ArrayList<Window>();
|
||||
int[] fStatus = new int[29];
|
||||
HashMap<CabFunction,FunctionButton> functionsHM = new HashMap<CabFunction,FunctionButton>();
|
||||
Throttle throttle;
|
||||
PImage cabImage;
|
||||
String cabFile;
|
||||
Window editWindow;
|
||||
InputBox cabNumInput;
|
||||
|
||||
CabButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, int cab, Throttle throttle){
|
||||
super(null, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, str(cab), ButtonType.NORMAL);
|
||||
this.cab=cab;
|
||||
this.throttle=throttle;
|
||||
cabButtons.add(this);
|
||||
reg=cabButtons.size();
|
||||
cabFile=("cab-"+cab+".jpg");
|
||||
cabImage=loadImage(cabFile);
|
||||
name="Cab"+cab;
|
||||
cabsHM.put(name,this);
|
||||
colorMode(HSB,255);
|
||||
editWindow = new Window(xPos-(bWidth/2),yPos-(bHeight/2),bWidth,bHeight,color(baseHue,255,255),color(baseHue,255,125));
|
||||
cabNumInput = new InputBox(this);
|
||||
|
||||
speedXML=autoPilotXML.getChild(name);
|
||||
if(speedXML==null){
|
||||
speedXML=autoPilotXML.addChild(name);
|
||||
speedXML.setContent(ThrottleSpeed.STOP.name());
|
||||
}
|
||||
|
||||
cabDefaultXML=cabDefaultsXML.getChild(name);
|
||||
if(cabDefaultXML==null){
|
||||
cabDefaultXML=cabDefaultsXML.addChild(name);
|
||||
}
|
||||
|
||||
} // CabButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
super.display();
|
||||
|
||||
imageMode(CENTER);
|
||||
fill(30);
|
||||
rect(xPos+bWidth/2+30,yPos,42,20);
|
||||
stroke(backgroundColor);
|
||||
line(xPos+bWidth/2+23,yPos-10,xPos+bWidth/2+23,yPos+10);
|
||||
line(xPos+bWidth/2+37,yPos-10,xPos+bWidth/2+37,yPos+10);
|
||||
textFont(throttleFont,22);
|
||||
if(speed>0)
|
||||
fill(0,255,0);
|
||||
else if(speed<0)
|
||||
fill(255,0,0);
|
||||
else
|
||||
fill(255,255,0);
|
||||
text(nf(abs(speed),3),xPos+bWidth/2+30,yPos);
|
||||
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void functionButtonWindow(int xPos, int yPos, int kWidth, int kHeight, color backgroundColor, color outlineColor){
|
||||
if(windowList.size()==1) // there is already one defined window and another is requested -- add a NextFunctionsButton to the original window
|
||||
new NextFunctionsButton(fbWindow, this, kWidth/2, kHeight+5, 40, 15, 60, 8, "More...");
|
||||
|
||||
fbWindow=new Window(xPos,yPos,kWidth,kHeight,backgroundColor,outlineColor);
|
||||
windowList.add(fbWindow);
|
||||
|
||||
if(windowList.size()>1) // there are at least two defined windows -- add a NextFunctionsButton to this window
|
||||
new NextFunctionsButton(fbWindow, this, kWidth/2, kHeight+5, 40, 15, 60, 8, "More...");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setFunction(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, int fNum, String name, ButtonType buttonType, CabFunction ... cFunc){
|
||||
new FunctionButton(fbWindow,xPos,yPos,bWidth,bHeight,baseHue,fontSize,this,fNum,name,buttonType,cFunc);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void activateFunction(CabFunction cFunc, boolean s){
|
||||
if(functionsHM.containsKey(cFunc))
|
||||
functionsHM.get(cFunc).activateFunction(s);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOn(){
|
||||
if(throttle.cabButton!=null){
|
||||
throttle.cabButton.fbWindow.close();
|
||||
throttle.cabButton.turnOff();
|
||||
}
|
||||
|
||||
super.turnOn();
|
||||
fbWindow.show();
|
||||
throttle.cabButton=this;
|
||||
opCabInput.setIntValue(cab);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOff(){
|
||||
super.turnOff();
|
||||
fbWindow.close();
|
||||
throttle.cabButton=null;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void shiftPressed(){
|
||||
autoPilot.parkCab(this);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void rightClick(){
|
||||
editWindow.open();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setThrottle(int tPos){
|
||||
aPort.write("<t"+reg+" "+cab+" "+abs(tPos)+" "+int(tPos>0)+">");
|
||||
|
||||
if(throttleSpeed!=ThrottleSpeed.STOP)
|
||||
throttleDefaultsXML.setInt(throttleSpeed.name(),tPos);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setThrottle(ThrottleSpeed throttleSpeed){
|
||||
this.throttleSpeed=throttleSpeed;
|
||||
setThrottle(throttleDefaultsXML.getInt(throttleSpeed.name()));
|
||||
speedXML.setContent(throttleSpeed.name());
|
||||
activateFunction(CabFunction.F_LIGHT,true);
|
||||
activateFunction(CabFunction.R_LIGHT,true);
|
||||
activateFunction(CabFunction.D_LIGHT,true);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setThrottleDefaults(int fullSpeed, int slowSpeed, int reverseSpeed, int reverseSlowSpeed){
|
||||
|
||||
throttleDefaultsXML=cabDefaultXML.getChild("throttleDefaults");
|
||||
|
||||
if(throttleDefaultsXML==null){
|
||||
throttleDefaultsXML=cabDefaultXML.addChild("throttleDefaults");
|
||||
throttleDefaultsXML.setInt(ThrottleSpeed.FULL.name(),fullSpeed);
|
||||
throttleDefaultsXML.setInt(ThrottleSpeed.SLOW.name(),slowSpeed);
|
||||
throttleDefaultsXML.setInt(ThrottleSpeed.REVERSE.name(),reverseSpeed);
|
||||
throttleDefaultsXML.setInt(ThrottleSpeed.REVERSE_SLOW.name(),reverseSlowSpeed);
|
||||
throttleDefaultsXML.setInt(ThrottleSpeed.STOP.name(),0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setSidingDefaults(RouteButton sidingRoute, int parkingSensor, int sidingSensor){
|
||||
this.sidingRoute=sidingRoute;
|
||||
this.parkingSensor=parkingSensor;
|
||||
this.sidingSensor=sidingSensor;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void stopThrottle(){
|
||||
aPort.write("<t"+reg+" "+cab+" -1 0>");
|
||||
throttleSpeed=ThrottleSpeed.STOP;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
String toString(){
|
||||
return(name);
|
||||
}
|
||||
|
||||
} // CabButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: FunctionButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class FunctionButton extends RectButton{
|
||||
int fNum;
|
||||
CabButton cabButton;
|
||||
String name;
|
||||
int oneShotCount;
|
||||
int fPolarity;
|
||||
|
||||
FunctionButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, CabButton cabButton, int fNum, String name, ButtonType buttonType, CabFunction[] cFunc){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, name, buttonType);
|
||||
this.fNum=abs(fNum)%29; // ensures fNum is always between 0 and 28, inclusive
|
||||
this.cabButton=cabButton;
|
||||
this.name=name;
|
||||
for(int i=0;i<cFunc.length;i++)
|
||||
cabButton.functionsHM.put(cFunc[i],this);
|
||||
if(buttonType==ButtonType.REVERSE)
|
||||
this.fPolarity=1;
|
||||
} // FunctionButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
|
||||
if(buttonType==ButtonType.ONESHOT && oneShotCount>0){
|
||||
oneShotCount--;
|
||||
isOn=true;
|
||||
}
|
||||
else
|
||||
isOn=(cabButton.fStatus[fNum]!=fPolarity);
|
||||
|
||||
super.display();
|
||||
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOn(){
|
||||
activateFunction(true);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOff(){
|
||||
activateFunction(false);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void released(){
|
||||
if(buttonType==ButtonType.HOLD)
|
||||
turnOff();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void activateFunction(boolean s){
|
||||
int f=0;
|
||||
int e=0;
|
||||
|
||||
if(s){
|
||||
cabButton.fStatus[fNum]=1-fPolarity;
|
||||
if(buttonType==ButtonType.ONESHOT){
|
||||
fPolarity=1-fPolarity;
|
||||
oneShotCount=1;
|
||||
}
|
||||
} else{
|
||||
cabButton.fStatus[fNum]=fPolarity;
|
||||
}
|
||||
|
||||
if(fNum<5){ // functions F0-F4 are single byte instructions of form 1-0-0-F0-F4-F3-F2-F1
|
||||
f=(1<<7)
|
||||
+(cabButton.fStatus[0]<<4)
|
||||
+(cabButton.fStatus[4]<<3)
|
||||
+(cabButton.fStatus[3]<<2)
|
||||
+(cabButton.fStatus[2]<<1)
|
||||
+cabButton.fStatus[1];
|
||||
} else if(fNum<9){ // functions F5-F8 are single byte instructions of form 1-0-1-1-F8-F7-F6-F5
|
||||
f=(1<<7)
|
||||
+(1<<5)
|
||||
+(1<<4)
|
||||
+(cabButton.fStatus[8]<<3)
|
||||
+(cabButton.fStatus[7]<<2)
|
||||
+(cabButton.fStatus[6]<<1)
|
||||
+cabButton.fStatus[5];
|
||||
} else if(fNum<13){ // functions F9-F12 are single byte instructions of form 1-0-1-0-F12-F11-F10-F9
|
||||
f=(1<<7)
|
||||
+(1<<5)
|
||||
+(cabButton.fStatus[12]<<3)
|
||||
+(cabButton.fStatus[11]<<2)
|
||||
+(cabButton.fStatus[10]<<1)
|
||||
+cabButton.fStatus[9];
|
||||
} else if(fNum<21){ // functions F13-F20 are two-byte instructions of form 0xDE followed by F20-F19-F18-F17-F16-F15-F14-F13
|
||||
f=222; // 0xDE
|
||||
e=(cabButton.fStatus[20]<<7)
|
||||
+(cabButton.fStatus[19]<<6)
|
||||
+(cabButton.fStatus[18]<<5)
|
||||
+(cabButton.fStatus[17]<<4)
|
||||
+(cabButton.fStatus[16]<<3)
|
||||
+(cabButton.fStatus[15]<<2)
|
||||
+(cabButton.fStatus[14]<<1)
|
||||
+cabButton.fStatus[13];
|
||||
} else if(fNum<29){ // functions F21-F28 are two-byte instructions of form 0xDF followed by F28-F27-F26-F25-F24-F23-F22-F21
|
||||
f=223; // 0xDF
|
||||
e=(cabButton.fStatus[28]<<7)
|
||||
+(cabButton.fStatus[27]<<6)
|
||||
+(cabButton.fStatus[26]<<5)
|
||||
+(cabButton.fStatus[25]<<4)
|
||||
+(cabButton.fStatus[24]<<3)
|
||||
+(cabButton.fStatus[23]<<2)
|
||||
+(cabButton.fStatus[22]<<1)
|
||||
+cabButton.fStatus[21];
|
||||
}
|
||||
|
||||
if(fNum<13)
|
||||
aPort.write("<f"+cabButton.cab+" "+f+">");
|
||||
else
|
||||
aPort.write("<f"+cabButton.cab+" "+f+" "+e+">");
|
||||
|
||||
} // activateFunction
|
||||
|
||||
} // FunctionButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: NextFunctionsButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class NextFunctionsButton extends RectButton{
|
||||
CabButton cButton;
|
||||
|
||||
NextFunctionsButton(Window window, CabButton cButton, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, bText, ButtonType.ONESHOT);
|
||||
this.cButton=cButton;
|
||||
} // PowerButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
super.pressed();
|
||||
cButton.fbWindow.close();
|
||||
cButton.fbWindow=throttleA.cabButton.windowList.get((throttleA.cabButton.windowList.indexOf(throttleA.cabButton.fbWindow)+1)%throttleA.cabButton.windowList.size());
|
||||
cButton.fbWindow.open();
|
||||
}
|
||||
|
||||
} // NextFunctionsButton Class
|
||||
139
DCC-Centrale/Firmware/Controller/DCCpp_Controller/dRoutes.pde
Normal file
@@ -0,0 +1,139 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Class for Route Button
|
||||
//
|
||||
// RouteButton - creates a button to activate one or more Track Buttons
|
||||
// that in turn set one or more TURNOUTS or CROSSOVERS to either an
|
||||
// open or closed position representing a specific track route
|
||||
// - tracks may also be added to a route button so that they are highlighted
|
||||
// on the screen when the route button is first selected
|
||||
// - track highlights will be color-coded to indicate whether each
|
||||
// turnout or crossover that in in the route is already set properly,
|
||||
// or needs to be toggled if that route is activiated
|
||||
//
|
||||
// - two types of route buttons are supported:
|
||||
//
|
||||
// * large stand-alone button with a text label indicated the name of the route
|
||||
// * small button placed on a track where the route is obvious and does
|
||||
// not require a name (such as at the end of a siding)
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RouteButton extends DccComponent{
|
||||
int xPos, yPos;
|
||||
int kWidth, kHeight;
|
||||
String label="";
|
||||
boolean routeOn=false;
|
||||
ArrayList<TrackButton> aTrackButtons = new ArrayList<TrackButton>();
|
||||
ArrayList<TrackButton> bTrackButtons = new ArrayList<TrackButton>();
|
||||
ArrayList<Track> rTracks = new ArrayList<Track>();
|
||||
|
||||
RouteButton(Track refTrack, int kWidth, int kHeight){
|
||||
this.xPos=int((refTrack.x[0]+refTrack.x[1])/2.0*refTrack.layout.sFactor+refTrack.layout.xCorner);
|
||||
this.yPos=int((refTrack.y[0]+refTrack.y[1])/2.0*refTrack.layout.sFactor+refTrack.layout.yCorner);
|
||||
this.kWidth=kWidth;
|
||||
this.kHeight=kHeight;
|
||||
dccComponents.add(this);
|
||||
}
|
||||
|
||||
RouteButton(int xPos, int yPos, int kWidth, int kHeight, String label){
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.kWidth=kWidth;
|
||||
this.kHeight=kHeight;
|
||||
this.label=label;
|
||||
dccComponents.add(this);
|
||||
|
||||
} // RouteButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void addTrackButton(TrackButton trackButton, int tPos){
|
||||
if(tPos==0){ // specifies that this track button should be set to A when route selected
|
||||
aTrackButtons.add(trackButton);
|
||||
trackButton.aRouteButtons.add(this);
|
||||
} else if (tPos==1) { // specifies that this track button should be set to B when route selected
|
||||
bTrackButtons.add(trackButton);
|
||||
trackButton.bRouteButtons.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void addTrack(Track track){
|
||||
rTracks.add(track);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
if(label.equals("")){
|
||||
ellipseMode(CENTER);
|
||||
if(routeOn)
|
||||
fill(color(0,255,0));
|
||||
else
|
||||
fill(color(0,150,0));
|
||||
noStroke();
|
||||
ellipse(xPos,yPos,kWidth/2,kHeight/2);
|
||||
} else{
|
||||
ellipseMode(CENTER);
|
||||
if(routeOn)
|
||||
fill(color(0,200,200));
|
||||
else
|
||||
fill(color(0,100,100));
|
||||
noStroke();
|
||||
ellipse(xPos,yPos,kWidth,kHeight);
|
||||
textFont(buttonFont,12);
|
||||
textAlign(CENTER,CENTER);
|
||||
fill(color(0));
|
||||
text(label,xPos,yPos);
|
||||
}
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
if(selectedComponent==null && (mouseX-xPos)*(mouseX-xPos)/(kWidth*kWidth/4.0)+(mouseY-yPos)*(mouseY-yPos)/(kHeight*kHeight/4.0)<=1){
|
||||
cursorType=HAND;
|
||||
selectedComponent=this;
|
||||
for(Track track : rTracks){
|
||||
track.hStatus=1;
|
||||
}
|
||||
}
|
||||
|
||||
else if(previousComponent==this){
|
||||
for(Track track : rTracks){
|
||||
track.hStatus=0;
|
||||
}
|
||||
}
|
||||
|
||||
} // check
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
for(TrackButton trackButton : aTrackButtons){
|
||||
if(trackButton.rEnabled)
|
||||
trackButton.pressed(0);
|
||||
}
|
||||
for(TrackButton trackButton : bTrackButtons){
|
||||
if(trackButton.rEnabled)
|
||||
trackButton.pressed(1);
|
||||
}
|
||||
routeOn=true;
|
||||
} // pressed
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void shiftPressed(){
|
||||
for(TrackButton trackButton : aTrackButtons){
|
||||
if(trackButton.rEnabled)
|
||||
trackButton.pressed(1);
|
||||
}
|
||||
for(TrackButton trackButton : bTrackButtons){
|
||||
if(trackButton.rEnabled)
|
||||
trackButton.pressed(0);
|
||||
}
|
||||
routeOn=false;
|
||||
} // shiftPressed
|
||||
|
||||
} // RouteButton Class
|
||||
815
DCC-Centrale/Firmware/Controller/DCCpp_Controller/dSensors.pde
Normal file
@@ -0,0 +1,815 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Classes for Sensors and AutoPilot Control
|
||||
//
|
||||
// AutoPilot Button - automaticaly operates three cabs in a pattern
|
||||
// by which each cab travels out to the sky bridge,
|
||||
// reverses course, and comes back into the inner
|
||||
// reversing loop after passing through the crossover
|
||||
// - status is saved between session
|
||||
// - clicking this button RESUMES a previous session
|
||||
// or stops the current session
|
||||
// - resumption implies all cabs, turnouts, and sensors are in
|
||||
// the exact same position as before the session was halted
|
||||
// - shift-clicking this button STARTS a new session
|
||||
// - starting a new seesion assume all cabs are in their start position
|
||||
// but sensors and turnouts will be automatically reset
|
||||
//
|
||||
// TrackSensor - defines a track sensor that triggers when the first car of a train passes, and
|
||||
// then again when the last car of that same train passes.
|
||||
// - creates a track sensor button on the track layout where ther sensor is located
|
||||
// - a given track sensor is defined to be "on" once an initial trigger is received from passage
|
||||
// of first the car of a train, and defined to be "off" once a second trigger is received from
|
||||
// passage of last car of that same train
|
||||
// - if the on/off status of a track sensor button seems out of sync with the actual train,
|
||||
// user can manually toggle the sensor "on" or "off" by clicking the appropriate sensor button
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class AutoPilotButton extends RectButton{
|
||||
int[] cabs={8601,6021,1506,622,1202,54}; // list of all cabs to be included in autoPilot - order does not matter since it will be randomized
|
||||
ArrayList<CabButton> cabList = new ArrayList<CabButton>();
|
||||
int phase=0;
|
||||
int tCount=0;
|
||||
int crossOver=0;
|
||||
AutoProgram program=AutoProgram.NONE;
|
||||
XML cabListXML, phaseXML, tCountXML, crossOverXML, programXML;
|
||||
int safetyTimer;
|
||||
|
||||
AutoPilotButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText);
|
||||
}
|
||||
|
||||
AutoPilotButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, bText, ButtonType.NORMAL);
|
||||
|
||||
phaseXML=autoPilotXML.getChild("Phase");
|
||||
if(phaseXML==null){
|
||||
phaseXML=autoPilotXML.addChild("Phase");
|
||||
phaseXML.setContent(str(phase));
|
||||
} else{
|
||||
phase=int(phaseXML.getContent());
|
||||
}
|
||||
|
||||
tCountXML=autoPilotXML.getChild("TCount");
|
||||
if(tCountXML==null){
|
||||
tCountXML=autoPilotXML.addChild("TCount");
|
||||
tCountXML.setContent(str(tCount));
|
||||
} else{
|
||||
tCount=int(tCountXML.getContent());
|
||||
}
|
||||
|
||||
crossOverXML=autoPilotXML.getChild("CrossOver");
|
||||
if(crossOverXML==null){
|
||||
crossOverXML=autoPilotXML.addChild("CrossOver");
|
||||
crossOverXML.setContent(str(crossOver));
|
||||
} else{
|
||||
crossOver=int(crossOverXML.getContent());
|
||||
}
|
||||
|
||||
programXML=autoPilotXML.getChild("Program");
|
||||
if(programXML==null){
|
||||
programXML=autoPilotXML.addChild("Program");
|
||||
programXML.setContent(program.name);
|
||||
} else{
|
||||
program=AutoProgram.index(programXML.getContent());
|
||||
}
|
||||
|
||||
cabListXML=autoPilotXML.getChild("CabList");
|
||||
if(cabListXML==null){
|
||||
cabListXML=autoPilotXML.addChild("CabList");
|
||||
cabListXML.setContent(join(nf(cabs,0)," "));
|
||||
}
|
||||
|
||||
for(int i: int(split(trim(cabListXML.getContent())," ")))
|
||||
cabList.add(cabsHM.get("Cab"+i));
|
||||
|
||||
updateDiagBox();
|
||||
|
||||
} // AutoButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
super.display();
|
||||
|
||||
textAlign(CENTER,CENTER);
|
||||
textFont(messageFont,12);
|
||||
fill(color(255));
|
||||
text(program.name,xPos+xWindow(),yPos+yWindow()+bHeight/2+10);
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
|
||||
if(isOn){
|
||||
turnOff();
|
||||
return;
|
||||
}
|
||||
|
||||
if(program==AutoProgram.NONE){
|
||||
msgBoxMain.setMessage("Can't resume Auto Pilot until program specified!",color(50,50,200));
|
||||
return;
|
||||
}
|
||||
|
||||
for(CabButton cb : cabList) // set throttles of all cabs specified in current program to prior values
|
||||
cb.setThrottle(ThrottleSpeed.index(cb.speedXML.getContent()));
|
||||
|
||||
if(program.equals(AutoProgram.AUTO_CLEAN))
|
||||
cleaningCab.turnOn();
|
||||
|
||||
msgBoxMain.setMessage("Auto Pilot Resuming",color(50,50,200));
|
||||
safetyTimer=millis();
|
||||
turnOn();
|
||||
} // pressed
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void init(){
|
||||
phase=0;
|
||||
tCount=0;
|
||||
crossOver=0;
|
||||
|
||||
for(TrackSensor ts : sensorsHM.values())
|
||||
ts.reset();
|
||||
|
||||
cabList.clear();
|
||||
for(int i:cabs)
|
||||
cabList.add(cabsHM.get("Cab"+i));
|
||||
|
||||
for(int i=0;i<3;i++) // randomize list
|
||||
cabList.add(0,cabList.remove(int(random(i,cabList.size()))));
|
||||
|
||||
updateCabList();
|
||||
|
||||
for(CabButton cb : cabList) // halt all cabs specified in full autopilot program
|
||||
cb.setThrottle(ThrottleSpeed.STOP);
|
||||
|
||||
rButtonReset.pressed();
|
||||
cabList.get(0).sidingRoute.pressed(); // set siding turnouts so they are aligned for first cab
|
||||
rButtonSpiral.pressed();
|
||||
rButton12.pressed();
|
||||
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.FULL); // start first cab!
|
||||
|
||||
msgBoxMain.setMessage("Auto Pilot Engaged",color(50,50,200));
|
||||
updateDiagBox();
|
||||
|
||||
} // init()
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void clean(){
|
||||
|
||||
if(isOn){
|
||||
msgBoxMain.setMessage("Auto Pilot already engaged!",color(50,50,200));
|
||||
return;
|
||||
}
|
||||
|
||||
cleaningCab.turnOn(); // turn on cleaning car
|
||||
cabList.clear();
|
||||
cabList.add(cabsHM.get("Cab"+2004)); // assumes cab 2004 is pulling cleaning car
|
||||
updateCabList();
|
||||
phase=100;
|
||||
phaseXML.setContent(str(phase));
|
||||
tButton10.pressed(0);
|
||||
|
||||
for(TrackSensor ts : sensorsHM.values())
|
||||
ts.reset();
|
||||
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.FULL); // start throttle for cab
|
||||
msgBoxMain.setMessage("Auto Clean Engaged",color(50,50,200));
|
||||
setProgram(AutoProgram.AUTO_CLEAN);
|
||||
safetyTimer=millis();
|
||||
turnOn();
|
||||
|
||||
} // clean
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void parkCab(CabButton selectedCab){
|
||||
|
||||
if(selectedCab.parkingSensor==0){
|
||||
msgBoxMain.setMessage("Auto Park not available for Cab "+selectedCab.cab,color(50,50,200));
|
||||
return;
|
||||
}
|
||||
|
||||
if(isOn){
|
||||
msgBoxMain.setMessage("Auto Pilot already engaged!",color(50,50,200));
|
||||
return;
|
||||
}
|
||||
|
||||
cabList.clear();
|
||||
cabList.add(selectedCab);
|
||||
updateCabList();
|
||||
phase=42;
|
||||
phaseXML.setContent(str(phase));
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.FULL); // start throttle for cab selected --- do not modify throttles for any other cabs
|
||||
msgBoxMain.setMessage("Auto Park Engaged for Cab "+selectedCab.cab,color(50,50,200));
|
||||
setProgram(AutoProgram.SINGLE_CAB_PARK);
|
||||
safetyTimer=millis();
|
||||
turnOn();
|
||||
|
||||
} // parkCab
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void shiftPressed(){
|
||||
|
||||
if(!isOn){
|
||||
setProgram(AutoProgram.ALL_CABS_RUN);
|
||||
safetyTimer=millis();
|
||||
turnOn();
|
||||
msgBoxMain.setMessage("Starting Auto Pilot...",color(50,50,200));
|
||||
buttonQueue.add(this);
|
||||
} else
|
||||
if(program==AutoProgram.ALL_CABS_RUN){
|
||||
msgBoxMain.setMessage("Switching to Auto Park",color(50,50,200));
|
||||
setProgram(AutoProgram.ALL_CABS_PARK);
|
||||
} else{
|
||||
msgBoxMain.setMessage("Auto Park or other program already engaged!",color(50,50,200));
|
||||
}
|
||||
} // shiftPressed
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOff(){
|
||||
super.turnOff();
|
||||
|
||||
msgBoxMain.setMessage("Auto Pilot Disengaged",color(50,50,200));
|
||||
|
||||
for(CabButton cb : cabList) // halt (but without updating XML) all cabs specified in current program only
|
||||
cb.stopThrottle();
|
||||
|
||||
if(program.equals(AutoProgram.AUTO_CLEAN))
|
||||
cleaningCab.turnOff();
|
||||
|
||||
if(program.equals(AutoProgram.SINGLE_CAB_RUN)){
|
||||
aPort.write("<u>");
|
||||
setProgram(AutoProgram.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void updateCabList(){
|
||||
|
||||
cabListXML.setContent("");
|
||||
for(CabButton cb : cabList)
|
||||
cabListXML.setContent(cabListXML.getContent()+cb.cab+" ");
|
||||
|
||||
cabListXML.setContent(trim(cabListXML.getContent()));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void process(int s, boolean isActive){
|
||||
|
||||
int lastPhase;
|
||||
|
||||
if(!isOn || program.equals(AutoProgram.SINGLE_CAB_RUN))
|
||||
return;
|
||||
|
||||
if(!isActive)
|
||||
s=-s;
|
||||
|
||||
lastPhase=phase;
|
||||
|
||||
switch(s){
|
||||
|
||||
case 1:
|
||||
if(phase==3){
|
||||
rButtonBridge.pressed();
|
||||
phase=4;
|
||||
} else
|
||||
if(phase==4){
|
||||
phase=5;
|
||||
} else
|
||||
if(phase==5){
|
||||
phase=6;
|
||||
} else
|
||||
if(phase==10){
|
||||
crossOver++;
|
||||
if(crossOver==2){
|
||||
cabList.get(0).stopThrottle();
|
||||
// cabList.get(0).activateFunction(CabFunction.HORN,true);
|
||||
// cabList.get(0).activateFunction(CabFunction.HORN,false);
|
||||
} else{
|
||||
cabList.get(0).activateFunction(CabFunction.S_HORN,true);
|
||||
}
|
||||
} else
|
||||
if(phase==11){
|
||||
phase=12;
|
||||
} else
|
||||
if(phase==13){
|
||||
phase=14;
|
||||
} else
|
||||
if(phase==40){
|
||||
tButton20.pressed(0);
|
||||
phase=41;
|
||||
}
|
||||
break;
|
||||
|
||||
case -1:
|
||||
if(phase==2){
|
||||
tCount++;
|
||||
} else
|
||||
if(phase==120){
|
||||
phase=42;
|
||||
tButton20.pressed(1);
|
||||
tButton10.pressed(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if(phase==0){
|
||||
tButton40.routeDisabled();
|
||||
cabList.get(1).sidingRoute.pressed();
|
||||
tButton40.routeEnabled();
|
||||
cabList.get(1).setThrottle(ThrottleSpeed.FULL);
|
||||
phase=1;
|
||||
// } else
|
||||
// if(phase==10 || phase==11){
|
||||
// cabList.get(2).setFunction(CabFunction.HORN,false);
|
||||
} else
|
||||
if(phase==30){
|
||||
tButton50.pressed(1);
|
||||
phase=31;
|
||||
}
|
||||
break;
|
||||
|
||||
case -2:
|
||||
if(phase==2){
|
||||
tCount++;
|
||||
if(tCount==1)
|
||||
cabList.get(1).setThrottle(ThrottleSpeed.STOP);
|
||||
} else
|
||||
if(phase==9){
|
||||
tButton30.pressed(1);
|
||||
tCount++;
|
||||
} else
|
||||
if(phase==10){
|
||||
if(crossOver>0){
|
||||
crossOver--;
|
||||
}
|
||||
if(crossOver==1){
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.FULL);
|
||||
// cabList.get(0).activateFunction(CabFunction.S_HORN,true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if(phase==7){
|
||||
tButton30.pressed(0);
|
||||
phase=8;
|
||||
} else
|
||||
if(phase==10){
|
||||
crossOver++;
|
||||
if(crossOver==2){
|
||||
cabList.get(2).stopThrottle();
|
||||
// cabList.get(2).activateFunction(CabFunction.HORN,true);
|
||||
// cabList.get(2).activateFunction(CabFunction.HORN,false);
|
||||
} else{
|
||||
cabList.get(2).activateFunction(CabFunction.S_HORN,true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case -3:
|
||||
if(phase==110){
|
||||
phase++;
|
||||
tButton30.pressed(1);
|
||||
} else
|
||||
if(phase==111||phase==112){
|
||||
phase++;
|
||||
} else
|
||||
if(phase==113){
|
||||
phase++;
|
||||
tButton30.pressed(0);
|
||||
tButton40.pressed(1);
|
||||
tButton1.pressed(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if(phase==1){
|
||||
tButton40.routeDisabled();
|
||||
cabList.get(2).sidingRoute.pressed();
|
||||
tButton40.routeEnabled();
|
||||
cabList.get(2).setThrottle(ThrottleSpeed.FULL);
|
||||
phase=2;
|
||||
} else
|
||||
if(phase==8){
|
||||
tButton40.pressed(0);
|
||||
phase=9;
|
||||
// } else
|
||||
// if(phase==10){
|
||||
// cabList.get(0).setFunction(CabFunction.HORN,false);
|
||||
} else
|
||||
if(phase==12){
|
||||
tButton4.pressed(1); // set reversing loop
|
||||
tButton7.pressed(0);
|
||||
phase=20; // start "parking" phase, then resume pattern
|
||||
} else
|
||||
if((phase==21 || phase==31 || phase==42) && cabList.get(0).parkingSensor==4){
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.SLOW);
|
||||
phase++;
|
||||
} else
|
||||
if(phase==41){
|
||||
tButton50.pressed(0);
|
||||
tButton4.pressed(1); // set reversing loop
|
||||
tButton7.pressed(0);
|
||||
phase=42;
|
||||
}
|
||||
break;
|
||||
|
||||
case -4:
|
||||
if(phase==10){
|
||||
if(crossOver>0){
|
||||
crossOver--;
|
||||
}
|
||||
if(crossOver==1){
|
||||
cabList.get(2).setThrottle(ThrottleSpeed.FULL);
|
||||
// cabList.get(2).activateFunction(CabFunction.S_HORN,true);
|
||||
}
|
||||
cabList.get(1).setThrottle(ThrottleSpeed.FULL); // just in case cab-1 was stopped on bridge
|
||||
tButton40.pressed(1);
|
||||
tButton1.pressed(0);
|
||||
tButton20.pressed(1);
|
||||
crossOver=0;
|
||||
phase=11;
|
||||
} else
|
||||
if((phase==22 || phase==32 || phase==43) && cabList.get(0).parkingSensor==4){
|
||||
phase++;
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.STOP);
|
||||
cabList.get(0).sidingRoute.shiftPressed();
|
||||
delay(500);
|
||||
cabList.get(0).sidingRoute.pressed();
|
||||
sensorsHM.get(cabList.get(0).sidingSensor).pressed(false);
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.REVERSE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if(phase==6){
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.SLOW);
|
||||
} else
|
||||
if(phase==14){
|
||||
cabList.get(1).setThrottle(ThrottleSpeed.SLOW);
|
||||
} else
|
||||
if(phase==42 && cabList.get(0).parkingSensor==5){
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.SLOW);
|
||||
phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
case -5:
|
||||
if(phase==6){
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.STOP);
|
||||
phase=7;
|
||||
} else
|
||||
if(phase==14){
|
||||
cabList.get(1).setThrottle(ThrottleSpeed.STOP);
|
||||
cabList.add(cabList.remove(0)); // move cab-0 to end of list
|
||||
updateCabList();
|
||||
phase=7; // start next cycle
|
||||
} else
|
||||
if(phase==43 && cabList.get(0).parkingSensor==5){
|
||||
phase++;
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.STOP);
|
||||
cabList.get(0).sidingRoute.shiftPressed();
|
||||
delay(500);
|
||||
cabList.get(0).sidingRoute.pressed();
|
||||
sensorsHM.get(cabList.get(0).sidingSensor).pressed(false);
|
||||
delay(100);
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.REVERSE);
|
||||
} else
|
||||
if(phase==100){
|
||||
phase++;
|
||||
tButton10.pressed(1);
|
||||
tButton30.pressed(1);
|
||||
tButton50.pressed(1);
|
||||
tButton4.pressed(0);
|
||||
tButton20.pressed(0);
|
||||
} else
|
||||
if(phase==101||phase==102){
|
||||
phase++;
|
||||
} else
|
||||
if(phase==103){
|
||||
phase++;
|
||||
tButton20.pressed(1);
|
||||
tButton50.pressed(0);
|
||||
} else
|
||||
if(phase==104||phase==105){
|
||||
phase++;
|
||||
} else
|
||||
if(phase==106){
|
||||
phase++;
|
||||
tButton10.pressed(0);
|
||||
} else
|
||||
if(phase==107||phase==108){
|
||||
phase++;
|
||||
} else
|
||||
if(phase==109){
|
||||
phase++;
|
||||
tButton20.pressed(0);
|
||||
tButton30.pressed(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case 6:
|
||||
if(phase==10){
|
||||
cabList.get(1).stopThrottle(); // wait on bridge until cab-0 clears sensor 4
|
||||
}
|
||||
break;
|
||||
|
||||
case -6:
|
||||
if(phase==9){
|
||||
tCount++;
|
||||
tButton8.pressed();
|
||||
}
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
if(phase==23 || phase==33 || phase==44){
|
||||
phase++;
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.REVERSE_SLOW);
|
||||
}
|
||||
break;
|
||||
|
||||
case -7:
|
||||
case -8:
|
||||
case -9:
|
||||
case -10:
|
||||
case -12:
|
||||
case -13:
|
||||
case -14:
|
||||
if(phase==24 || phase==34 || phase==45){
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.STOP);
|
||||
sensorsHM.get(cabList.get(0).parkingSensor).pressed(false);
|
||||
tButton40.pressed(1);
|
||||
if(program==AutoProgram.SINGLE_CAB_PARK||program==AutoProgram.AUTO_CLEAN){
|
||||
phase=51; // phase must have previously been 45
|
||||
turnOff();
|
||||
} else
|
||||
if(program==AutoProgram.ALL_CABS_PARK){
|
||||
cabList.add(0,cabList.remove(2)); // move cab-2 to beginning of list, making it cab-0
|
||||
updateCabList();
|
||||
phase+=6; // start parking routine at either phase=30, or if second cab just parked then phase=40, or if third cab finished parking phase=51
|
||||
if(phase==51){
|
||||
turnOff();
|
||||
}
|
||||
} else{
|
||||
cabList.add(3,cabList.remove(int(random(3,cabList.size())))); // pick random cab to be next to leave siding
|
||||
updateCabList();
|
||||
tButton40.routeDisabled();
|
||||
cabList.get(3).sidingRoute.pressed();
|
||||
tButton40.routeEnabled();
|
||||
cabList.get(3).setThrottle(ThrottleSpeed.FULL);
|
||||
phase=25;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 11:
|
||||
if(phase==20){
|
||||
phase=21;
|
||||
} else
|
||||
if((phase==21 || phase==31 || phase==42) && cabList.get(0).parkingSensor==11){
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.SLOW);
|
||||
phase++;
|
||||
} else
|
||||
if(phase==25){
|
||||
phase=26;
|
||||
} else
|
||||
if(phase==26){
|
||||
phase=13;
|
||||
}
|
||||
break;
|
||||
|
||||
case -11:
|
||||
if((phase==22 || phase==32 || phase==43) && cabList.get(0).parkingSensor==11){
|
||||
phase++;
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.STOP);
|
||||
cabList.get(0).sidingRoute.shiftPressed();
|
||||
delay(500);
|
||||
cabList.get(0).sidingRoute.pressed();
|
||||
sensorsHM.get(cabList.get(0).sidingSensor).pressed(false);
|
||||
delay(100);
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.REVERSE);
|
||||
} else
|
||||
if(phase==114||phase==115){
|
||||
phase++;
|
||||
} else
|
||||
if(phase==116){
|
||||
phase++;
|
||||
tButton4.pressed(1);
|
||||
tButton7.pressed(0);
|
||||
tButton5.pressed(0);
|
||||
tButton20.pressed(0);
|
||||
tButton8.pressed(0);
|
||||
} else
|
||||
if(phase==117){
|
||||
phase++;
|
||||
tButton40.pressed(0);
|
||||
} else
|
||||
if(phase==118||phase==119){
|
||||
phase++;
|
||||
}
|
||||
break;
|
||||
|
||||
} // switch t
|
||||
|
||||
if(phase==2 && tCount==2){
|
||||
cabList.get(1).setThrottle(ThrottleSpeed.FULL); // just in case cab-1 was previously stopped to wait for cab-0 to catch up
|
||||
rButton10.pressed();
|
||||
rButton11.pressed();
|
||||
tCount=0;
|
||||
phase=3;
|
||||
} else
|
||||
|
||||
if(phase==9 && tCount==2){
|
||||
cabList.get(0).setThrottle(ThrottleSpeed.FULL);
|
||||
tButton20.pressed(0);
|
||||
tButton4.pressed(0);
|
||||
tCount=0;
|
||||
crossOver=0;
|
||||
phase=10;
|
||||
}
|
||||
|
||||
phaseXML.setContent(str(phase));
|
||||
tCountXML.setContent(str(tCount));
|
||||
crossOverXML.setContent(str(crossOver));
|
||||
|
||||
updateDiagBox();
|
||||
|
||||
if(phase!=lastPhase) // there was an update of the phase
|
||||
safetyTimer=millis(); // reset timer
|
||||
|
||||
|
||||
} // process
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setProgram(AutoProgram p){
|
||||
program=p;
|
||||
programXML.setContent(program.name);
|
||||
updateDiagBox();
|
||||
saveXMLFlag=true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void updateDiagBox(){
|
||||
|
||||
String s="";
|
||||
|
||||
for(XML xml: autoPilotXML.getChildren()){
|
||||
if(!xml.getName().equals("#text"))
|
||||
s=s+(String.format("%10s",xml.getName())+" = "+xml.getContent()+"\n");
|
||||
}
|
||||
|
||||
msgAutoState.setMessage(s);
|
||||
|
||||
} // updateDiagBox
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void safetyCheck(){
|
||||
|
||||
int countDown;
|
||||
|
||||
if(!isOn || program.equals(AutoProgram.SINGLE_CAB_RUN))
|
||||
return;
|
||||
|
||||
countDown=120-int((millis()-safetyTimer)/1000);
|
||||
|
||||
msgAutoTimer.setMessage("Timer = "+countDown);
|
||||
|
||||
if(countDown<=0){
|
||||
powerButton.turnOff();
|
||||
turnOff();
|
||||
}
|
||||
|
||||
} // safetyCheck
|
||||
|
||||
} // AutoPilot Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: TrackSensor
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class TrackSensor extends Track{
|
||||
boolean isActive=false;
|
||||
boolean sensorDefault;
|
||||
int xPos, yPos;
|
||||
int mTime;
|
||||
int kWidth, kHeight;
|
||||
String sensorName;
|
||||
int sensorNum;
|
||||
XML sensorButtonXML;
|
||||
MessageBox msgBoxSensor;
|
||||
|
||||
TrackSensor(Track refTrack, int trackPoint, float tLength, int kWidth, int kHeight, int sensorNum, boolean sensorDefault){
|
||||
super(refTrack,trackPoint,tLength);
|
||||
this.kWidth=kWidth;
|
||||
this.kHeight=kHeight;
|
||||
this.xPos=int(x[1]*layout.sFactor+layout.xCorner);
|
||||
this.yPos=int(y[1]*layout.sFactor+layout.yCorner);
|
||||
this.sensorNum=sensorNum;
|
||||
sensorName="Sensor"+sensorNum;
|
||||
componentName=sensorName;
|
||||
this.sensorDefault=sensorDefault;
|
||||
sensorButtonXML=sensorButtonsXML.getChild(sensorName);
|
||||
if(sensorButtonXML==null){
|
||||
sensorButtonXML=sensorButtonsXML.addChild(sensorName);
|
||||
sensorButtonXML.setContent(str(isActive));
|
||||
} else{
|
||||
isActive=boolean(sensorButtonXML.getContent());
|
||||
}
|
||||
sensorsHM.put(sensorNum,this);
|
||||
msgBoxSensor=new MessageBox(sensorWindow,0,sensorNum*22+22,-1,0,color(175),18,"S-"+nf(sensorNum,2)+":",color(50,50,250));
|
||||
}
|
||||
|
||||
TrackSensor(Track refTrack, int trackPoint, float curveRadius, float curveAngleDeg, int kWidth, int kHeight, int sensorNum, boolean sensorDefault){
|
||||
super(refTrack,trackPoint,curveRadius,curveAngleDeg);
|
||||
this.kWidth=kWidth;
|
||||
this.kHeight=kHeight;
|
||||
this.xPos=int(x[1]*layout.sFactor+layout.xCorner);
|
||||
this.yPos=int(y[1]*layout.sFactor+layout.yCorner);
|
||||
this.sensorNum=sensorNum;
|
||||
this.sensorDefault=sensorDefault;
|
||||
sensorName="Sensor"+sensorNum;
|
||||
componentName=sensorName;
|
||||
sensorButtonXML=sensorButtonsXML.getChild(sensorName);
|
||||
if(sensorButtonXML==null){
|
||||
sensorButtonXML=sensorButtonsXML.addChild(sensorName);
|
||||
sensorButtonXML.setContent(str(isActive));
|
||||
} else{
|
||||
isActive=boolean(sensorButtonXML.getContent());
|
||||
}
|
||||
sensorsHM.put(sensorNum,this);
|
||||
msgBoxSensor=new MessageBox(sensorWindow,0,sensorNum*22+22,-1,0,color(175),18,"S-"+nf(sensorNum,2)+":",color(50,50,250));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
ellipseMode(CENTER);
|
||||
|
||||
strokeWeight(1);
|
||||
stroke(color(255,255,0));
|
||||
noFill();
|
||||
|
||||
if(isActive)
|
||||
fill(color(50,50,200));
|
||||
|
||||
ellipse(xPos,yPos,kWidth/2,kHeight/2);
|
||||
} // display()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
pressed(!isActive);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(boolean isActive){
|
||||
this.isActive=isActive;
|
||||
autoPilot.process(sensorNum,isActive);
|
||||
sensorButtonXML.setContent(str(isActive));
|
||||
saveXMLFlag=true;
|
||||
if(isActive){
|
||||
msgBoxSensor.setMessage("S-"+nf(sensorNum,2)+": "+nf(hour(),2)+":"+nf(minute(),2)+":"+nf(second(),2)+" - "+nf((millis()-mTime)/1000.0,0,1)+" sec");
|
||||
mTime=millis();
|
||||
}
|
||||
|
||||
} // pressed
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void reset(){
|
||||
pressed(sensorDefault);
|
||||
|
||||
} // reset
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
if(selectedComponent==null && (mouseX-xPos)*(mouseX-xPos)/(kWidth*kWidth/4.0)+(mouseY-yPos)*(mouseY-yPos)/(kHeight*kHeight/4.0)<=1){
|
||||
cursorType=HAND;
|
||||
selectedComponent=this;
|
||||
}
|
||||
|
||||
} // check
|
||||
|
||||
} // TrackSensor Class
|
||||
264
DCC-Centrale/Firmware/Controller/DCCpp_Controller/dTracks.pde
Normal file
@@ -0,0 +1,264 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Classes for Layouts and Tracks
|
||||
//
|
||||
// Layout - defines a scaled region on the screen into which tracks
|
||||
// will be place using scaled coordinates
|
||||
//
|
||||
// Track - defines a curved or straight piece of track.
|
||||
// - placement on layout can be in absolute scaled coordinates
|
||||
// or linked to one end of a previously-defined track.
|
||||
// - tracks can be linked even across separate layouts
|
||||
// - define multiple overlapping tracks to create any type
|
||||
// of turnout, crossover, or other complex track
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Layout{
|
||||
int xCorner, yCorner;
|
||||
float sFactor;
|
||||
|
||||
Layout(int xCorner, int yCorner, int frameWidth, float layoutWidth, float layoutHeight){
|
||||
this.xCorner=xCorner;
|
||||
this.yCorner=yCorner;
|
||||
sFactor=float(frameWidth)/layoutWidth; // frameWidth in pixels, layoutWidth in mm, inches, cm, etc.
|
||||
} // Layout
|
||||
|
||||
Layout(Layout layout){
|
||||
this.xCorner=layout.xCorner;
|
||||
this.yCorner=layout.yCorner;
|
||||
this.sFactor=layout.sFactor;
|
||||
} // Layout
|
||||
|
||||
void copy(Layout layout){
|
||||
this.xCorner=layout.xCorner;
|
||||
this.yCorner=layout.yCorner;
|
||||
this.sFactor=layout.sFactor;
|
||||
} // copy
|
||||
|
||||
boolean equals(Layout layout){
|
||||
return((this.xCorner==layout.xCorner)&&(this.yCorner==layout.yCorner)&&(this.sFactor==layout.sFactor));
|
||||
} // equals
|
||||
|
||||
} // Layout Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Track extends DccComponent{
|
||||
float[] x = new float[2];
|
||||
float[] y = new float[2];
|
||||
float[] a = new float[2];
|
||||
color tColor;
|
||||
float xR, yR;
|
||||
float r;
|
||||
float aStart, aEnd;
|
||||
int tStatus=1; // specfies current track status (0=off/not visible, 1=on/visible)
|
||||
int hStatus=0; // specifies if current track is highlighted (1) or normal (0)
|
||||
Layout layout;
|
||||
|
||||
Track(Layout layout, float x, float y, float tLength, float angleDeg){
|
||||
this.x[0]=x;
|
||||
this.y[0]=y;
|
||||
this.a[1]=angleDeg/360.0*TWO_PI;
|
||||
this.a[0]=this.a[1]+PI;
|
||||
if(this.a[0]>=TWO_PI)
|
||||
this.a[0]-=TWO_PI;
|
||||
this.x[1]=this.x[0]+cos(this.a[1])*tLength;
|
||||
this.y[1]=this.y[0]-sin(this.a[1])*tLength;
|
||||
this.layout=layout;
|
||||
this.tColor=color(255,255,0);
|
||||
dccComponents.add(this);
|
||||
} // Track - straight, absolute
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Track(Track track, int trackPoint, float tLength, Layout layout){
|
||||
this.x[0]=track.x[trackPoint%2];
|
||||
this.y[0]=track.y[trackPoint%2];
|
||||
this.a[1]=track.a[trackPoint%2];
|
||||
this.a[0]=this.a[1]+PI;
|
||||
if(this.a[0]>=TWO_PI)
|
||||
this.a[0]-=TWO_PI;
|
||||
this.x[1]=this.x[0]+cos(this.a[1])*tLength;
|
||||
this.y[1]=this.y[0]-sin(this.a[1])*tLength;
|
||||
this.layout=layout;
|
||||
this.tColor=color(255,255,0);
|
||||
dccComponents.add(this);
|
||||
} // Track - straight, relative, Layout specified
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Track(Track track, int trackPoint, float tLength){
|
||||
this.x[0]=track.x[trackPoint%2];
|
||||
this.y[0]=track.y[trackPoint%2];
|
||||
this.a[1]=track.a[trackPoint%2];
|
||||
this.a[0]=this.a[1]+PI;
|
||||
if(this.a[0]>=TWO_PI)
|
||||
this.a[0]-=TWO_PI;
|
||||
this.x[1]=this.x[0]+cos(this.a[1])*tLength;
|
||||
this.y[1]=this.y[0]-sin(this.a[1])*tLength;
|
||||
this.layout=track.layout;
|
||||
this.tColor=color(255,255,0);
|
||||
dccComponents.add(this);
|
||||
} // Track - straight, relative, no Layout specified
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Track(Layout layout, float x, float y, float curveRadius, float curveAngleDeg, float angleDeg){
|
||||
float thetaR, thetaA;
|
||||
int d;
|
||||
|
||||
thetaR=curveAngleDeg/360.0*TWO_PI;
|
||||
thetaA=angleDeg/360.0*TWO_PI;
|
||||
d=(thetaR>0)?1:-1;
|
||||
|
||||
this.x[0]=x;
|
||||
this.y[0]=y;
|
||||
|
||||
this.a[0]=thetaA+PI;
|
||||
if(this.a[0]>=TWO_PI)
|
||||
|
||||
this.a[0]-=TWO_PI;
|
||||
this.a[1]=thetaA+thetaR;
|
||||
if(this.a[1]>=TWO_PI)
|
||||
this.a[1]-=TWO_PI;
|
||||
if(this.a[1]<0)
|
||||
this.a[1]+=TWO_PI;
|
||||
|
||||
this.r=curveRadius;
|
||||
|
||||
this.xR=this.x[0]-d*this.r*sin(thetaA);
|
||||
this.yR=this.y[0]-d*this.r*cos(thetaA);
|
||||
|
||||
this.x[1]=this.xR+d*this.r*sin(thetaA+thetaR);
|
||||
this.y[1]=this.yR+d*this.r*cos(thetaA+thetaR);
|
||||
|
||||
if(d==1){
|
||||
this.aEnd=PI/2-thetaA;
|
||||
this.aStart=this.aEnd-thetaR;
|
||||
}else{
|
||||
this.aStart=1.5*PI-thetaA;
|
||||
this.aEnd=this.aStart-thetaR;
|
||||
}
|
||||
|
||||
this.layout=layout;
|
||||
this.tColor=color(255,255,0);
|
||||
dccComponents.add(this);
|
||||
} // Track - curved, absolute
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Track(Track track, int trackPoint, float curveRadius, float curveAngleDeg, Layout layout){
|
||||
float thetaR, thetaA;
|
||||
int d;
|
||||
|
||||
thetaR=curveAngleDeg/360.0*TWO_PI;
|
||||
thetaA=track.a[trackPoint%2];
|
||||
d=(thetaR>0)?1:-1;
|
||||
|
||||
this.x[0]=track.x[trackPoint%2];
|
||||
this.y[0]=track.y[trackPoint%2];
|
||||
|
||||
this.a[0]=thetaA+PI;
|
||||
if(this.a[0]>=TWO_PI)
|
||||
|
||||
this.a[0]-=TWO_PI;
|
||||
this.a[1]=thetaA+thetaR;
|
||||
if(this.a[1]>=TWO_PI)
|
||||
this.a[1]-=TWO_PI;
|
||||
if(this.a[1]<0)
|
||||
this.a[1]+=TWO_PI;
|
||||
|
||||
this.r=curveRadius;
|
||||
|
||||
this.xR=this.x[0]-d*this.r*sin(thetaA);
|
||||
this.yR=this.y[0]-d*this.r*cos(thetaA);
|
||||
|
||||
this.x[1]=this.xR+d*this.r*sin(thetaA+thetaR);
|
||||
this.y[1]=this.yR+d*this.r*cos(thetaA+thetaR);
|
||||
|
||||
if(d==1){
|
||||
this.aEnd=PI/2-thetaA;
|
||||
this.aStart=this.aEnd-thetaR;
|
||||
}else{
|
||||
this.aStart=1.5*PI-thetaA;
|
||||
this.aEnd=this.aStart-thetaR;
|
||||
}
|
||||
|
||||
this.layout=layout;
|
||||
this.tColor=color(255,255,0);
|
||||
dccComponents.add(this);
|
||||
} // Track - curved, relative, Layout specified
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Track(Track track, int trackPoint, float curveRadius, float curveAngleDeg){
|
||||
float thetaR, thetaA;
|
||||
int d;
|
||||
|
||||
thetaR=curveAngleDeg/360.0*TWO_PI;
|
||||
thetaA=track.a[trackPoint%2];
|
||||
d=(thetaR>0)?1:-1;
|
||||
|
||||
this.x[0]=track.x[trackPoint%2];
|
||||
this.y[0]=track.y[trackPoint%2];
|
||||
|
||||
this.a[0]=thetaA+PI;
|
||||
if(this.a[0]>=TWO_PI)
|
||||
|
||||
this.a[0]-=TWO_PI;
|
||||
this.a[1]=thetaA+thetaR;
|
||||
if(this.a[1]>=TWO_PI)
|
||||
this.a[1]-=TWO_PI;
|
||||
if(this.a[1]<0)
|
||||
this.a[1]+=TWO_PI;
|
||||
|
||||
this.r=curveRadius;
|
||||
|
||||
this.xR=this.x[0]-d*this.r*sin(thetaA);
|
||||
this.yR=this.y[0]-d*this.r*cos(thetaA);
|
||||
|
||||
this.x[1]=this.xR+d*this.r*sin(thetaA+thetaR);
|
||||
this.y[1]=this.yR+d*this.r*cos(thetaA+thetaR);
|
||||
|
||||
if(d==1){
|
||||
this.aEnd=PI/2-thetaA;
|
||||
this.aStart=this.aEnd-thetaR;
|
||||
}else{
|
||||
this.aStart=1.5*PI-thetaA;
|
||||
this.aEnd=this.aStart-thetaR;
|
||||
}
|
||||
|
||||
this.layout=track.layout;
|
||||
this.tColor=color(255,255,0);
|
||||
dccComponents.add(this);
|
||||
} // Track - curved, relative, no Layout specified
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
|
||||
if(tStatus==1){ // track is visible
|
||||
if(hStatus==1) // track is highlighted
|
||||
stroke(color(0,255,0));
|
||||
else
|
||||
stroke(color(255,255,0));
|
||||
} else{ // track is not visible
|
||||
if(hStatus==1) // track is highlighted
|
||||
stroke(color(255,0,0));
|
||||
else
|
||||
stroke(color(80,80,0));
|
||||
}
|
||||
|
||||
strokeWeight(3);
|
||||
ellipseMode(RADIUS);
|
||||
noFill();
|
||||
if(r==0){
|
||||
line(x[0]*layout.sFactor+layout.xCorner,y[0]*layout.sFactor+layout.yCorner,x[1]*layout.sFactor+layout.xCorner,y[1]*layout.sFactor+layout.yCorner);
|
||||
}
|
||||
else{
|
||||
arc(xR*layout.sFactor+layout.xCorner,yR*layout.sFactor+layout.yCorner,r*layout.sFactor,r*layout.sFactor,aStart,aEnd);
|
||||
}
|
||||
} // display()
|
||||
|
||||
} // Track Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
153
DCC-Centrale/Firmware/Controller/DCCpp_Controller/dTurnouts.pde
Normal file
@@ -0,0 +1,153 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Class for Track Button
|
||||
//
|
||||
// TrackButton - creates a TURNOUT or CROSSOVER by grouping two sets of
|
||||
// of pre-specified tracks
|
||||
// - one set of tracks defines the state of the turnout
|
||||
// or crossover in the "open" position
|
||||
// - the other set of tracks defines the state of the turnout
|
||||
// or crossover in the "closed" position
|
||||
// - a clickable but otherwise invisible button (the Track Button)
|
||||
// located near the center of the turnout or crossover
|
||||
// toggles between the closed and open positions
|
||||
//
|
||||
//
|
||||
// - when toggled, TrackButton will:
|
||||
//
|
||||
// * reset the colors of each set of tracks to
|
||||
// indicate whether the turnour or crossover
|
||||
// is "open" or "closed"
|
||||
//
|
||||
// * reset the color of any route buttons that use this
|
||||
// track button
|
||||
//
|
||||
// * send a DCC ACCESSORY COMMAND to the DCC++ Base Station
|
||||
// using the Accessory Address and Accessory Number
|
||||
// specified for this Track Button
|
||||
//
|
||||
// In accordance with NMRA DCC Standards, accessory decoders
|
||||
// are controlled using 12 bits messages. The first 11 form
|
||||
// a main address (9 bits) and a sub address (2 bits). Depending
|
||||
// on the specifics of a particular manufacturers decoder, these
|
||||
// 11 bits can be interpreted as a single address (0-2047) or
|
||||
// as a main address (0-511) with 4 sub addresses (0-3). Some decoders
|
||||
// may respond to any address matching the first 9 bits; others may
|
||||
// also consider the two sub address bits. In any case, Track Button
|
||||
// can be used to send the correct combination of 11 bits to sucessfully
|
||||
// communicate with the decoder.
|
||||
//
|
||||
// The 12th bit is generally considered to be the data bit that is used
|
||||
// to toggle the accessory either on or off. In the case of a decoder
|
||||
// driving a turnout or crossover, this data bit is used to toggle between
|
||||
// the open and closed positions.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class TrackButton extends DccComponent{
|
||||
int xPos, yPos;
|
||||
int kWidth, kHeight;
|
||||
int buttonStatus=0;
|
||||
int id;
|
||||
boolean rEnabled=true;
|
||||
ArrayList<Track> aTracks = new ArrayList<Track>();
|
||||
ArrayList<Track> bTracks = new ArrayList<Track>();
|
||||
ArrayList<RouteButton> aRouteButtons = new ArrayList<RouteButton>();
|
||||
ArrayList<RouteButton> bRouteButtons = new ArrayList<RouteButton>();
|
||||
|
||||
TrackButton(int kWidth, int kHeight, int id){
|
||||
this.kWidth=kWidth;
|
||||
this.kHeight=kHeight;
|
||||
this.id=id;
|
||||
this.componentName="T"+id;
|
||||
trackButtonsHM.put(id,this);
|
||||
dccComponents.add(this);
|
||||
} // FunctionButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void addTrack(Track track, int tPos){
|
||||
int n=aTracks.size()+bTracks.size();
|
||||
this.xPos=int((this.xPos*n+(track.x[0]+track.x[1])/2.0*track.layout.sFactor+track.layout.xCorner)/(n+1.0));
|
||||
this.yPos=int((this.yPos*n+(track.y[0]+track.y[1])/2.0*track.layout.sFactor+track.layout.yCorner)/(n+1.0));
|
||||
|
||||
if(tPos==0){ // specifies that this track should be considered part of aTracks
|
||||
track.tStatus=1-buttonStatus;
|
||||
aTracks.add(track);
|
||||
} else if (tPos==1) { // specifies that this track should be considered part of bTracks
|
||||
track.tStatus=buttonStatus;
|
||||
bTracks.add(track);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
if(buttonStatus==0){
|
||||
for(Track track : bTracks)
|
||||
track.display();
|
||||
for(Track track : aTracks)
|
||||
track.display();
|
||||
} else {
|
||||
for(Track track : aTracks)
|
||||
track.display();
|
||||
for(Track track : bTracks)
|
||||
track.display();
|
||||
}
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
if(selectedComponent==null && (mouseX-xPos)*(mouseX-xPos)/(kWidth*kWidth/4.0)+(mouseY-yPos)*(mouseY-yPos)/(kHeight*kHeight/4.0)<=1){
|
||||
cursorType=HAND;
|
||||
selectedComponent=this;
|
||||
}
|
||||
} // check
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void routeEnabled(){
|
||||
rEnabled=true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void routeDisabled(){
|
||||
rEnabled=false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
pressed(1-buttonStatus);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(int buttonStatus){
|
||||
aPort.write("<T"+id+" "+buttonStatus+">");
|
||||
delay(50);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void update(int buttonStatus){
|
||||
|
||||
this.buttonStatus=buttonStatus;
|
||||
|
||||
for(Track track : aTracks)
|
||||
track.tStatus=1-buttonStatus;
|
||||
for(Track track : bTracks)
|
||||
track.tStatus=buttonStatus;
|
||||
|
||||
if(buttonStatus==0){
|
||||
for(RouteButton routeButton : bRouteButtons)
|
||||
routeButton.routeOn=false;
|
||||
} else {
|
||||
for(RouteButton routeButton : aRouteButtons)
|
||||
routeButton.routeOn=false;
|
||||
}
|
||||
|
||||
} // update
|
||||
|
||||
} // TrackButton Class
|
||||
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 648 KiB |
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<dccStatus>
|
||||
<serialPort>Emulator</serialPort>
|
||||
<sensorButtons>
|
||||
<Sensor1>false</Sensor1>
|
||||
<Sensor2>false</Sensor2>
|
||||
<Sensor3>true</Sensor3>
|
||||
<Sensor4>false</Sensor4>
|
||||
<Sensor5>false</Sensor5>
|
||||
<Sensor6>false</Sensor6>
|
||||
<Sensor7>true</Sensor7>
|
||||
<Sensor8>true</Sensor8>
|
||||
<Sensor9>false</Sensor9>
|
||||
<Sensor10>false</Sensor10>
|
||||
<Sensor11>true</Sensor11>
|
||||
<Sensor12>false</Sensor12>
|
||||
<Sensor13>true</Sensor13>
|
||||
<Sensor14>false</Sensor14>
|
||||
</sensorButtons>
|
||||
<autoPilot>
|
||||
<Cab2004>FULL</Cab2004>
|
||||
<Cab622>FULL</Cab622>
|
||||
<Cab8601>STOP</Cab8601>
|
||||
<Cab6021>STOP</Cab6021>
|
||||
<Cab54>STOP</Cab54>
|
||||
<Cab1202>FULL</Cab1202>
|
||||
<Cab1506>STOP</Cab1506>
|
||||
<Phase>42</Phase>
|
||||
<TCount>0</TCount>
|
||||
<CrossOver>0</CrossOver>
|
||||
<Program>SINGLE CAB PARK</Program>
|
||||
<CabList>622</CabList>
|
||||
<Cab2904>STOP</Cab2904>
|
||||
</autoPilot>
|
||||
<cabDefaults>
|
||||
<Cab2004>
|
||||
<throttleDefaults FULL="100" REVERSE="-50" REVERSE_SLOW="-45" SLOW="50" STOP="0"/>
|
||||
</Cab2004>
|
||||
<Cab622>
|
||||
<throttleDefaults FULL="50" REVERSE="-20" REVERSE_SLOW="-16" SLOW="30" STOP="0"/>
|
||||
</Cab622>
|
||||
<Cab8601>
|
||||
<throttleDefaults FULL="77" REVERSE="-34" REVERSE_SLOW="-30" SLOW="46" STOP="0"/>
|
||||
</Cab8601>
|
||||
<Cab6021>
|
||||
<throttleDefaults FULL="55" REVERSE="-25" REVERSE_SLOW="-15" SLOW="25" STOP="0"/>
|
||||
</Cab6021>
|
||||
<Cab54>
|
||||
<throttleDefaults FULL="59" REVERSE="-17" REVERSE_SLOW="-10" SLOW="14" STOP="0"/>
|
||||
</Cab54>
|
||||
<Cab1202>
|
||||
<throttleDefaults FULL="32" REVERSE="-18" REVERSE_SLOW="-15" SLOW="25" STOP="0"/>
|
||||
</Cab1202>
|
||||
<Cab1506>
|
||||
<throttleDefaults FULL="80" REVERSE="-30" REVERSE_SLOW="-27" SLOW="42" STOP="0"/>
|
||||
</Cab1506>
|
||||
<Cab2904>
|
||||
<throttleDefaults FULL="100" REVERSE="-50" REVERSE_SLOW="-45" SLOW="50" STOP="0"/>
|
||||
</Cab2904>
|
||||
</cabDefaults>
|
||||
<arduinoPort>/dev/tty.usbmodem1431</arduinoPort>
|
||||
<serverList>192.168.1.169</serverList>
|
||||
</dccStatus>
|
||||
@@ -0,0 +1,336 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Event Handlers
|
||||
//
|
||||
// Top-level processing of mouse, keyboard, and serial events.
|
||||
// Most of the real functionality is contained in other methods,
|
||||
// functions, and classes called by these handlers
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mouseDragged(){
|
||||
if(selectedComponent!=null)
|
||||
selectedComponent.drag();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mousePressed(){
|
||||
|
||||
if(activeInputBox!=null){
|
||||
for(InputBox inputBox : activeInputBox.linkedBoxes)
|
||||
inputBox.setIntValue(activeInputBox.getIntValue());
|
||||
}
|
||||
|
||||
activeInputBox=null;
|
||||
if(selectedComponent!=null){
|
||||
if (keyPressed == true && key == CODED){
|
||||
if(keyCode == SHIFT){
|
||||
selectedComponent.shiftPressed();
|
||||
} else if(keyCode == CONTROL){
|
||||
msgBoxMain.setMessage("Component Name: "+selectedComponent.componentName,color(30,30,150));
|
||||
}
|
||||
}
|
||||
else if(mouseButton==LEFT){
|
||||
selectedComponent.pressed();
|
||||
} else {
|
||||
selectedComponent.rightClick();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void mouseReleased(){
|
||||
if(selectedComponent!=null)
|
||||
selectedComponent.released();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void keyPressed(){
|
||||
keyCommand(key, keyCode);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void keyReleased(){
|
||||
keyCommandReleased(key, keyCode);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void serialEvent(Serial p){
|
||||
receivedString(p.readString());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void clientEvent(Client c){
|
||||
String s;
|
||||
s=c.readStringUntil('>');
|
||||
if(s!=null)
|
||||
receivedString(s);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void receivedString(String s){
|
||||
if(s.charAt(0)!='<')
|
||||
return;
|
||||
|
||||
String c=s.substring(2,s.length()-1);
|
||||
|
||||
switch(s.charAt(1)){
|
||||
|
||||
case 'i':
|
||||
baseID=c;
|
||||
msgBoxMain.setMessage("Found "+baseID,color(0,150,0));
|
||||
break;
|
||||
|
||||
case '*':
|
||||
msgBoxDiagIn.setMessage(c,color(30,30,150));
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
String[] cs=splitTokens(c,"|");
|
||||
callBacks.get(int(cs[0])).execute(int(cs[1]),cs[2]);
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
int[] n=int(splitTokens(c));
|
||||
if(n[0]>cabButtons.size())
|
||||
break;
|
||||
CabButton t=cabButtons.get(n[0]-1);
|
||||
if(n[2]==1)
|
||||
t.speed=n[1];
|
||||
else
|
||||
t.speed=-n[1];
|
||||
break;
|
||||
|
||||
case 'Q':
|
||||
if(sensorsHM.get(int(c))!=null){
|
||||
sensorsHM.get(int(c)).pressed();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Y':
|
||||
int[] h1=int(splitTokens(c));
|
||||
if(remoteButtonsHM.get(h1[0])!=null){
|
||||
if(h1[1]==1)
|
||||
remoteButtonsHM.get(h1[0]).turnOn();
|
||||
else
|
||||
remoteButtonsHM.get(h1[0]).turnOff();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
int[] h=int(splitTokens(c));
|
||||
|
||||
if(trackButtonsHM.get(h[0])!=null){
|
||||
trackButtonsHM.get(h[0]).update(h[1]);
|
||||
} else if(remoteButtonsHM.get(h[0])!=null){
|
||||
if(h[1]==((remoteButtonsHM.get(h[0]).buttonType==ButtonType.T_COMMAND)?1:0))
|
||||
remoteButtonsHM.get(h[0]).turnOn();
|
||||
else
|
||||
remoteButtonsHM.get(h[0]).turnOff();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
int[] z=int(splitTokens(c));
|
||||
color tempColor;
|
||||
tempColor=color(z[0],z[1],z[2]);
|
||||
colorMode(HSB,1.0,1.0,1.0);
|
||||
ledColorButton.hue=hue(tempColor);
|
||||
ledColorButton.sat=saturation(tempColor);
|
||||
ledColorButton.val=brightness(tempColor);
|
||||
ledColorButton.update(0);
|
||||
colorMode(RGB,255);
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
autoPilot.cabList.clear();
|
||||
autoPilot.setProgram(AutoProgram.SINGLE_CAB_RUN);
|
||||
autoPilot.turnOn();
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if(c.equals("1")){
|
||||
powerButton.isOn=true;
|
||||
msgBoxMain.setMessage("Track Power On",color(30,30,150));
|
||||
} else if(c.equals("0")){
|
||||
powerButton.isOn=false;
|
||||
msgBoxMain.setMessage("Track Power Off",color(30,30,150));
|
||||
} else if(c.equals("2")){
|
||||
msgBoxMain.setMessage("MAIN Track Current Overload - Power Off",color(200,30,30));
|
||||
powerButton.isOn=false;
|
||||
} else if(c.equals("3")){
|
||||
msgBoxMain.setMessage("PROG Track Current Overload - Power Off",color(200,30,30));
|
||||
powerButton.isOn=false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
currentMeter.addSample(int(c));
|
||||
break;
|
||||
|
||||
}
|
||||
} // receivedString
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void keyCommand(char k, int kC){
|
||||
|
||||
if(activeInputBox!=null){
|
||||
activeInputBox.keyStroke(k, kC);
|
||||
return;
|
||||
}
|
||||
|
||||
if(k==CODED){
|
||||
switch(kC){
|
||||
case UP:
|
||||
if(throttleA.cabButton!=null){
|
||||
if(!keyHold)
|
||||
throttleA.pressed();
|
||||
throttleA.keyControl(1);
|
||||
}
|
||||
break;
|
||||
case DOWN:
|
||||
if(throttleA.cabButton!=null){
|
||||
if(!keyHold)
|
||||
throttleA.pressed();
|
||||
throttleA.keyControl(-1);
|
||||
}
|
||||
break;
|
||||
case LEFT:
|
||||
if(throttleA.cabButton!=null){
|
||||
throttleA.keyControl(0);
|
||||
}
|
||||
break;
|
||||
case RIGHT:
|
||||
if(throttleA.cabButton!=null){
|
||||
throttleA.cabButton.stopThrottle();
|
||||
}
|
||||
break;
|
||||
}
|
||||
} // key is coded
|
||||
|
||||
else{
|
||||
switch(k){
|
||||
case 'P':
|
||||
powerButton.turnOn();
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
aPort.write("<3>");
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
aPort.write("<2>");
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
powerButton.turnOff();
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
accWindow.toggle();
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
currentMeter.isOn=!currentMeter.isOn;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
extrasWindow.toggle();
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
autoWindow.toggle();
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
sensorWindow.toggle();
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
ledWindow.toggle();
|
||||
break;
|
||||
|
||||
case 's':
|
||||
portWindow.toggle();
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
helpWindow.toggle();
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
imageWindow.toggle();
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
diagWindow.toggle();
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
if(layoutBridge.equals(layout2))
|
||||
layoutBridge.copy(layout);
|
||||
else
|
||||
layoutBridge.copy(layout2);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
progWindow.toggle();
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
opWindow.toggle();
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
if(throttleA.cabButton!=null){
|
||||
throttleA.cabButton.fbWindow.close();
|
||||
throttleA.cabButton.fbWindow=throttleA.cabButton.windowList.get((throttleA.cabButton.windowList.indexOf(throttleA.cabButton.fbWindow)+1)%throttleA.cabButton.windowList.size());
|
||||
throttleA.cabButton.fbWindow.open();
|
||||
}
|
||||
break;
|
||||
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
cabButtons.get(int(k)-int('1')).pressed();
|
||||
break;
|
||||
|
||||
}
|
||||
} // key not coded
|
||||
|
||||
keyHold=true;
|
||||
} // keyCommand
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void keyCommandReleased(char k, int kC){
|
||||
|
||||
keyHold=false;
|
||||
|
||||
if(k==CODED){
|
||||
switch(kC){
|
||||
}
|
||||
} // key is coded
|
||||
|
||||
else{
|
||||
switch(k){
|
||||
}
|
||||
} // key not coded
|
||||
|
||||
} // keyCommandReleased
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
168
DCC-Centrale/Firmware/Controller/DCCpp_Controller/gButtons.pde
Normal file
@@ -0,0 +1,168 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Generic Ellipse and Rectangle Buttons
|
||||
//
|
||||
// EllipseButton - base class for creating simple buttons
|
||||
// - operating buttons that extend EllipseButton should
|
||||
// over-ride these methods with functionality specific
|
||||
// to that button
|
||||
//
|
||||
// RectButton - variant of EllipseButton that define a rectanglular button
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class EllipseButton extends DccComponent{
|
||||
int bWidth, bHeight;
|
||||
int baseHue;
|
||||
color textColor;
|
||||
int fontSize;
|
||||
String bText;
|
||||
ButtonType buttonType;
|
||||
int remoteCode;
|
||||
boolean isOn=false;
|
||||
|
||||
EllipseButton(){
|
||||
this(width/2,height/2,80,50,100,color(0),16,"Button",ButtonType.NORMAL);
|
||||
}
|
||||
|
||||
EllipseButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, color textColor, int fontSize, String bText, ButtonType buttonType){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, textColor, fontSize, bText, buttonType);
|
||||
}
|
||||
|
||||
EllipseButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, color textColor, int fontSize, String bText, ButtonType buttonType){
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.bWidth=bWidth;
|
||||
this.bHeight=bHeight;
|
||||
this.bText=bText;
|
||||
this.fontSize=fontSize;
|
||||
this.baseHue=baseHue;
|
||||
this.textColor=textColor;
|
||||
this.window=window;
|
||||
this.buttonType=buttonType;
|
||||
if(window==null)
|
||||
dccComponents.add(this);
|
||||
else
|
||||
window.windowComponents.add(this);
|
||||
} // EllipseButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
colorMode(HSB,255);
|
||||
ellipseMode(CENTER);
|
||||
noStroke();
|
||||
fill(color(baseHue,255,isOn?255:125));
|
||||
ellipse(xPos+xWindow(),yPos+yWindow(),bWidth,bHeight);
|
||||
fill(textColor);
|
||||
textFont(buttonFont,fontSize);
|
||||
textAlign(CENTER,CENTER);
|
||||
text(bText,xPos+xWindow(),yPos+yWindow());
|
||||
if(buttonType==ButtonType.ONESHOT && isOn)
|
||||
turnOff();
|
||||
colorMode(RGB,255);
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
if(selectedComponent==null && (mouseX-xPos-xWindow())*(mouseX-xPos-xWindow())/(bWidth*bWidth/4.0)+(mouseY-yPos-yWindow())*(mouseY-yPos-yWindow())/(bHeight*bHeight/4.0)<=1){
|
||||
cursorType=HAND;
|
||||
selectedComponent=this;
|
||||
}
|
||||
} // check
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOn(){
|
||||
isOn=true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOff(){
|
||||
isOn=false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
|
||||
if(buttonType==ButtonType.T_COMMAND){
|
||||
aPort.write("<T"+remoteCode+" "+(isOn?"0>":"1>"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(buttonType==ButtonType.TI_COMMAND){
|
||||
aPort.write("<T"+remoteCode+" "+(isOn?"1>":"0>"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(buttonType==ButtonType.Z_COMMAND){
|
||||
aPort.write("<Z"+remoteCode+" "+(isOn?"0>":"1>"));
|
||||
return;
|
||||
}
|
||||
|
||||
if(isOn)
|
||||
turnOff();
|
||||
else
|
||||
turnOn();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void released(){
|
||||
if(buttonType==ButtonType.HOLD)
|
||||
turnOff();
|
||||
}
|
||||
|
||||
} // EllipseButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RectButton extends EllipseButton{
|
||||
|
||||
RectButton(){
|
||||
super(width/2,height/2,80,50,100,color(0),16,"Button",ButtonType.NORMAL);
|
||||
}
|
||||
|
||||
RectButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, color textColor, int fontSize, String bText, ButtonType buttonType){
|
||||
super(null, xPos, yPos, bWidth, bHeight, baseHue, textColor, fontSize, bText, buttonType);
|
||||
}
|
||||
|
||||
RectButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, color textColor, int fontSize, String bText, ButtonType buttonType){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, textColor, fontSize, bText, buttonType);
|
||||
}
|
||||
|
||||
RectButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, color textColor, int fontSize, String bText, ButtonType buttonType, int remoteCode){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, textColor, fontSize, bText, buttonType);
|
||||
this.remoteCode=remoteCode;
|
||||
remoteButtonsHM.put(remoteCode,this);
|
||||
} // RectangleButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
colorMode(HSB,255);
|
||||
rectMode(CENTER);
|
||||
noStroke();
|
||||
fill(color(baseHue,255,isOn?255:125));
|
||||
rect(xPos+xWindow(),yPos+yWindow(),bWidth,bHeight);
|
||||
fill(textColor);
|
||||
textFont(buttonFont,fontSize);
|
||||
textAlign(CENTER,CENTER);
|
||||
text(bText,xPos+xWindow(),yPos+yWindow());
|
||||
if(buttonType==ButtonType.ONESHOT && isOn)
|
||||
turnOff();
|
||||
colorMode(RGB,255);
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
if(selectedComponent==null && (mouseX>xPos+xWindow()-bWidth/2)&&(mouseX<xPos+xWindow()+bWidth/2)&&(mouseY>yPos+yWindow()-bHeight/2)&&(mouseY<yPos+yWindow()+bHeight/2)){
|
||||
cursorType=HAND;
|
||||
selectedComponent=this;
|
||||
}
|
||||
} // check
|
||||
|
||||
} // RectButton Class
|
||||
254
DCC-Centrale/Firmware/Controller/DCCpp_Controller/gTextBoxes.pde
Normal file
@@ -0,0 +1,254 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Generic Input/Output Text Boxes
|
||||
//
|
||||
// MessageBox - defines an output box for displaying text of specified
|
||||
// - size, color, and background
|
||||
//
|
||||
// InputBox - defines a box with text that can by input via the keyboard
|
||||
// - box size and allowable characters can be constrained
|
||||
// - text that is input is stored for later reference by other
|
||||
// classes and methods
|
||||
// - multiple boxes can be lniked so that they form a "tab" group
|
||||
// - clicking on any object outside the input box ends the input mode
|
||||
// - hitting "return" also ends the input mode
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class MessageBox extends DccComponent{
|
||||
int kWidth, kHeight;
|
||||
color boxColor;
|
||||
color msgColor;
|
||||
int fontSize;
|
||||
String msgText;
|
||||
|
||||
MessageBox(int xPos, int yPos, int kWidth, int kHeight, color boxColor, int fontSize){
|
||||
this(null, xPos, yPos, kWidth, kHeight, boxColor, fontSize);
|
||||
} // MessageBox
|
||||
|
||||
MessageBox(int xPos, int yPos, int kWidth, int kHeight, color boxColor, int fontSize, String msgText, color msgColor){
|
||||
this(null, xPos, yPos, kWidth, kHeight, boxColor, fontSize);
|
||||
setMessage(msgText, msgColor);
|
||||
} // MessageBox
|
||||
|
||||
MessageBox(Window window, int xPos, int yPos, int kWidth, int kHeight, color boxColor, int fontSize, String msgText, color msgColor){
|
||||
this(window, xPos, yPos, kWidth, kHeight, boxColor, fontSize);
|
||||
setMessage(msgText, msgColor);
|
||||
} // MessageBox
|
||||
|
||||
MessageBox(Window window, int xPos, int yPos, int kWidth, int kHeight, color boxColor, int fontSize){
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.kWidth=kWidth;
|
||||
this.kHeight=kHeight;
|
||||
this.msgText="";
|
||||
this.msgColor=color(0,0,255);
|
||||
this.fontSize=fontSize;
|
||||
this.boxColor=boxColor;
|
||||
this.window=window;
|
||||
if(window==null)
|
||||
dccComponents.add(this);
|
||||
else
|
||||
window.windowComponents.add(this);
|
||||
} // MessageBox
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
noStroke();
|
||||
rectMode(CENTER);
|
||||
fill(boxColor);
|
||||
rect(xPos+xWindow()-(kWidth<0?kWidth/2:0),yPos+yWindow(),abs(kWidth),kHeight);
|
||||
textFont(messageFont,fontSize);
|
||||
textAlign(kWidth<0?LEFT:CENTER,CENTER);
|
||||
fill(msgColor);
|
||||
text(msgText,xPos+xWindow(),yPos+yWindow());
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setMessage(String msgText, color msgColor){
|
||||
this.msgText=msgText;
|
||||
this.msgColor=msgColor;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setMessage(String msgText){
|
||||
this.msgText=msgText;
|
||||
}
|
||||
|
||||
} // MessageBox Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: InputBox
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class InputBox extends DccComponent{
|
||||
int kWidth, kHeight;
|
||||
int fontSize;
|
||||
color boxColor;
|
||||
color msgColor;
|
||||
String inputText="";
|
||||
int maxChars;
|
||||
Pattern regexp;
|
||||
InputType inputType;
|
||||
InputBox nextBox=null;
|
||||
CabButton cb=null;
|
||||
ArrayList<InputBox> linkedBoxes = new ArrayList<InputBox>();
|
||||
|
||||
InputBox(CabButton cb){
|
||||
this(cb.editWindow,4,cb.bHeight/2,cb.fontSize,color(255,0,255),color(0,0,0),4,InputType.DEC);
|
||||
this.cb=cb;
|
||||
setIntValue(cb.cab);
|
||||
}
|
||||
|
||||
InputBox(int xPos, int yPos, int fontSize, color boxColor, color msgColor, int maxChars, InputType inputType){
|
||||
this(null, xPos, yPos, fontSize, boxColor, msgColor, maxChars, inputType);
|
||||
}
|
||||
|
||||
InputBox(Window window, int xPos, int yPos, int fontSize, color boxColor, color msgColor, int maxChars, InputType inputType){
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.fontSize=fontSize;
|
||||
this.msgColor=msgColor;
|
||||
this.boxColor=boxColor;
|
||||
this.window=window;
|
||||
this.maxChars=maxChars;
|
||||
textFont(messageFont,fontSize);
|
||||
String s="0";
|
||||
for(int i=0;i<maxChars;i++,s+="0");
|
||||
this.kWidth=int(textWidth(s));
|
||||
this.kHeight=fontSize+4;
|
||||
this.inputType=inputType;
|
||||
linkedBoxes.add(this);
|
||||
regexp=regexp.compile(inputType.regexp);
|
||||
if(window==null)
|
||||
dccComponents.add(this);
|
||||
else
|
||||
window.windowComponents.add(this);
|
||||
} // InputBox
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
String textCursor;
|
||||
noStroke();
|
||||
rectMode(CENTER);
|
||||
if(activeInputBox==this)
|
||||
fill(255);
|
||||
else
|
||||
fill(boxColor);
|
||||
rect(xPos+xWindow()+kWidth/2,yPos+yWindow(),kWidth,kHeight);
|
||||
textFont(messageFont,fontSize);
|
||||
textAlign(LEFT,CENTER);
|
||||
fill(msgColor);
|
||||
if(activeInputBox!=this && inputText.length()==0)
|
||||
textCursor="?";
|
||||
else if(activeInputBox==this && (millis()/500)%2==1)
|
||||
textCursor="|";
|
||||
else
|
||||
textCursor="";
|
||||
text(inputText+textCursor,xPos+xWindow(),yPos+yWindow());
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
if(selectedComponent==null && (mouseX>xPos+xWindow())&&(mouseX<xPos+xWindow()+kWidth)&&(mouseY>yPos+yWindow()-kHeight/2)&&(mouseY<yPos+yWindow()+kHeight/2)){
|
||||
if(activeInputBox!=this)
|
||||
cursorType=TEXT;
|
||||
selectedComponent=this;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
activeInputBox=this;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setNextBox(InputBox nextBox){
|
||||
this.nextBox=nextBox;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void linkBox(InputBox inputBox){
|
||||
linkedBoxes=inputBox.linkedBoxes;
|
||||
linkedBoxes.add(this);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int getIntValue(){
|
||||
if(inputText.length()==0)
|
||||
return 0;
|
||||
if(inputType==InputType.DEC)
|
||||
return int(inputText);
|
||||
if(inputType==InputType.BIN)
|
||||
return unbinary(inputText);
|
||||
if(inputType==InputType.HEX)
|
||||
return unhex(inputText);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setIntValue(int v){
|
||||
if(inputType==InputType.DEC)
|
||||
inputText=str(v);
|
||||
else if(inputType==InputType.BIN)
|
||||
inputText=binary(v,8);
|
||||
else if(inputType==InputType.HEX)
|
||||
inputText=hex(v,2);
|
||||
else
|
||||
inputText="";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void resetValue(){
|
||||
inputText="";
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void keyStroke(char k, int kC){
|
||||
if(kC!=CODED){
|
||||
if(regexp.matcher(str(k)).find() && inputText.length()<maxChars){
|
||||
inputText+=k;
|
||||
} else if(k==BACKSPACE && inputText.length()>0){
|
||||
inputText=inputText.substring(0,inputText.length()-1);
|
||||
} else if(k==ENTER || k==RETURN){
|
||||
activeInputBox=null;
|
||||
for( InputBox inputBox : linkedBoxes)
|
||||
inputBox.setIntValue(getIntValue());
|
||||
if(cb!=null){
|
||||
cb.cab=getIntValue();
|
||||
cb.bText=str(cb.cab);
|
||||
cb.cabFile=("cab-"+cb.cab+".jpg");
|
||||
cb.cabImage=loadImage(cb.cabFile);
|
||||
cb.name="Cab"+cb.cab;
|
||||
cabsHM.put(cb.name,cb);
|
||||
cb.editWindow.close();
|
||||
}
|
||||
} else if(k==TAB){
|
||||
if(nextBox!=null)
|
||||
nextBox.pressed();
|
||||
else
|
||||
activeInputBox=null;
|
||||
for( InputBox inputBox : linkedBoxes)
|
||||
inputBox.setIntValue(getIntValue());
|
||||
if(cb!=null){
|
||||
setIntValue(cb.cab);
|
||||
cb.editWindow.close();
|
||||
}
|
||||
}
|
||||
} // kc!=CODED
|
||||
} // keyStroke
|
||||
|
||||
} // InputBox Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
291
DCC-Centrale/Firmware/Controller/DCCpp_Controller/gWindows.pde
Normal file
@@ -0,0 +1,291 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Generic Windows
|
||||
//
|
||||
// Window - creates a window box of a specified size, color, and
|
||||
// initial position into which other components can be placed
|
||||
// such as buttons, message boxes, and text boxes
|
||||
//
|
||||
// DragBar - creates a drag bar on window to allow it to be dragged
|
||||
// across screen
|
||||
//
|
||||
// CloseButton - creates close button on window that closes window box
|
||||
// - windows are normally opened by other buttons or key commands
|
||||
// defined elsewhere
|
||||
//
|
||||
// ImageWindow - extends Window to create a window bx into which
|
||||
// a single cab image tied to a specified throttle will be displayed
|
||||
//
|
||||
// JPGWindow - extends Window to create a generic window box for diplaying a single jpg image
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class Window extends DccComponent{
|
||||
int xPos, yPos;
|
||||
int kWidth, kHeight;
|
||||
color backgroundColor;
|
||||
color outlineColor;
|
||||
|
||||
ArrayList<DccComponent> windowComponents = new ArrayList<DccComponent>();
|
||||
|
||||
Window(int xPos, int yPos, int kWidth, int kHeight, color backgroundColor, color outlineColor){
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.kWidth=kWidth;
|
||||
this.kHeight=kHeight;
|
||||
this.backgroundColor=backgroundColor;
|
||||
this.outlineColor=outlineColor;
|
||||
} // Window
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
|
||||
rectMode(CORNER);
|
||||
fill(backgroundColor);
|
||||
strokeWeight(3);
|
||||
stroke(outlineColor);
|
||||
rect(xPos,yPos,kWidth,kHeight);
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
if(selectedComponent==null && (mouseX>xPos)&&(mouseX<xPos+kWidth)&&(mouseY>yPos)&&(mouseY<yPos+kHeight)){
|
||||
selectedComponent=this;
|
||||
}
|
||||
} // check
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
close();
|
||||
open();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void toggle(){
|
||||
if(dccComponents.contains(this))
|
||||
close();
|
||||
else
|
||||
open();
|
||||
} // toggle
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void open(){
|
||||
if(dccComponents.contains(this))
|
||||
return;
|
||||
|
||||
dccComponents.add(this); /// adds window and components to end of dccComponents --- will display last on top
|
||||
for(DccComponent windowComponent : windowComponents)
|
||||
dccComponents.add(windowComponent);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void show(){
|
||||
if(dccComponents.contains(this))
|
||||
return;
|
||||
|
||||
for(DccComponent windowComponent : windowComponents)
|
||||
dccComponents.add(0,windowComponent);
|
||||
dccComponents.add(0,this); // adds window and components to start of dccComponents --- will display first on bottom
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void close(){
|
||||
if(!dccComponents.contains(this))
|
||||
return;
|
||||
|
||||
for(DccComponent windowComponent : windowComponents)
|
||||
dccComponents.remove(windowComponent);
|
||||
dccComponents.remove(this);
|
||||
}
|
||||
|
||||
} // Window Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: DragBar
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class DragBar extends DccComponent{
|
||||
int xPos, yPos;
|
||||
int kWidth, kHeight;
|
||||
color backgroundColor;
|
||||
Window window;
|
||||
int xDrag, yDrag;
|
||||
|
||||
DragBar(Window window, int xPos, int yPos, int kWidth, int kHeight, color backgroundColor){
|
||||
this.window=window;
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.kWidth=kWidth;
|
||||
this.kHeight=kHeight;
|
||||
this.backgroundColor=backgroundColor;
|
||||
window.windowComponents.add(this);
|
||||
} // Window
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
rectMode(CORNER);
|
||||
fill(backgroundColor);
|
||||
noStroke();
|
||||
rect(xPos+window.xPos,yPos+window.yPos,kWidth,kHeight);
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
if(selectedComponent==null && (mouseX>xPos+window.xPos)&&(mouseX<xPos+window.xPos+kWidth)&&(mouseY>yPos+window.yPos)&&(mouseY<yPos+window.yPos+kHeight)){
|
||||
cursorType=MOVE;
|
||||
selectedComponent=this;
|
||||
}
|
||||
} // check
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
window.close();
|
||||
window.open();
|
||||
xDrag=mouseX-window.xPos;
|
||||
yDrag=mouseY-window.yPos;
|
||||
cursor(ARROW);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void drag(){
|
||||
window.xPos=mouseX-xDrag;
|
||||
window.yPos=mouseY-yDrag;
|
||||
}
|
||||
|
||||
} // DragBar Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: CloseButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CloseButton extends DccComponent{
|
||||
int xPos, yPos;
|
||||
int kWidth, kHeight;
|
||||
color backgroundColor;
|
||||
color lineColor;
|
||||
Window window;
|
||||
|
||||
CloseButton(Window window, int xPos, int yPos, int kWidth, int kHeight, color backgroundColor, color lineColor){
|
||||
this.window=window;
|
||||
this.xPos=xPos;
|
||||
this.yPos=yPos;
|
||||
this.kWidth=kWidth;
|
||||
this.kHeight=kHeight;
|
||||
this.backgroundColor=backgroundColor;
|
||||
this.lineColor=lineColor;
|
||||
window.windowComponents.add(this);
|
||||
} // Window
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
rectMode(CORNER);
|
||||
fill(backgroundColor);
|
||||
stroke(lineColor);
|
||||
strokeWeight(1);
|
||||
rect(xPos+window.xPos,yPos+window.yPos,kWidth,kHeight);
|
||||
line(xPos+window.xPos,yPos+window.yPos,xPos+window.xPos+kWidth,yPos+window.yPos+kHeight);
|
||||
line(xPos+window.xPos,yPos+window.yPos+kHeight,xPos+window.xPos+kWidth,yPos+window.yPos);
|
||||
} // display
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void check(){
|
||||
if(selectedComponent==null && (mouseX>xPos+window.xPos)&&(mouseX<xPos+window.xPos+kWidth)&&(mouseY>yPos+window.yPos)&&(mouseY<yPos+window.yPos+kHeight)){
|
||||
cursorType=HAND;
|
||||
selectedComponent=this;
|
||||
}
|
||||
} // check
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
window.close();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// void drag(){
|
||||
// }
|
||||
|
||||
} // CloseButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: ImageWindow
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ImageWindow extends Window{
|
||||
PImage img;
|
||||
Throttle throttle;
|
||||
int w,h;
|
||||
|
||||
ImageWindow(Throttle throttle, int w, int h, int xPos, int yPos, color outlineColor){
|
||||
super(xPos, yPos, w, h, color(255), outlineColor);
|
||||
new DragBar(this,0,0,w,10,outlineColor);
|
||||
new CloseButton(this,w-12,0,10,10,outlineColor,color(255,255,255));
|
||||
this.throttle=throttle;
|
||||
this.w=w;
|
||||
this.h=h;
|
||||
} // Window
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
super.display();
|
||||
if(throttle.cabButton==null){
|
||||
textAlign(CENTER,CENTER);
|
||||
fill(color(200,0,0));
|
||||
textFont(messageFont,20);
|
||||
text("PLEASE SELECT CAB TO DISPLAY IMAGE",xPos+w/2,yPos+h/2);
|
||||
} else if(throttle.cabButton.cabImage==null){
|
||||
textAlign(CENTER,CENTER);
|
||||
fill(color(200,0,0));
|
||||
textFont(messageFont,20);
|
||||
text("NO IMAGE FILE FOUND FOR THIS CAB",xPos+w/2,yPos+h/2);
|
||||
} else{
|
||||
imageMode(CORNER);
|
||||
image(throttle.cabButton.cabImage,xPos,yPos,w,h);
|
||||
}
|
||||
|
||||
} // display
|
||||
|
||||
} // ImageWindow Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: JPGWindow
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class JPGWindow extends Window{
|
||||
PImage img;
|
||||
int w,h;
|
||||
|
||||
JPGWindow(String JPGFile, int w, int h, int xPos, int yPos, color outlineColor){
|
||||
super(xPos, yPos, w, h, color(255), outlineColor);
|
||||
new DragBar(this,0,0,w,10,outlineColor);
|
||||
new CloseButton(this,w-12,0,10,10,outlineColor,color(255,255,255));
|
||||
img=loadImage(JPGFile);
|
||||
this.w=w;
|
||||
this.h=h;
|
||||
} // Window
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void display(){
|
||||
super.display();
|
||||
imageMode(CORNER);
|
||||
image(img,xPos,yPos,w,h);
|
||||
} // display
|
||||
|
||||
} // JPGWindow Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,439 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Programming Components
|
||||
//
|
||||
// All classes and methods related to programming mobile decoder
|
||||
// configuration variables (CVs)
|
||||
////
|
||||
// ProgWriteReadButton - sends a DCC PROGRAMMING COMMAND to the DCC++ Base Station
|
||||
// that either reads from, or writes to, a user-specified CV in a mobile
|
||||
// decoder on the Programming Track
|
||||
// - Note: cab numbers (mobile decoder addresses) are not used on the Programming
|
||||
// Track. Whatever locomotive is on the track will be programmed!
|
||||
// - users specify the CV to be written or read via an input box
|
||||
// - in the case of writing to a CV, three linked input boxes allow the user to
|
||||
// specify the byte in either HEX, DECIMAL, or BINARY formats
|
||||
// - in the case of reading from a CV, these three linked input boxes are updated
|
||||
// to display the results of the read in HEX, DECIMAL, and BINARY formats
|
||||
// - in the case of writing to a CV, the DCC++ Base Station automatically performs an
|
||||
// subsequent read to verify the byte was properly written
|
||||
//
|
||||
// ProgAddReadButton - sends a series of DCC PROGRAMMING COMMANDS to the DCC++ Base Station
|
||||
// that reads the following CVs from a mobile decoder on the Programming Track:
|
||||
//
|
||||
// * CV #1 - contains the short (single byte) cab address
|
||||
// * CV #17 - contains the high byte of a long (two byte) cab address
|
||||
// * CV #18 - contains the low byte of a long (two byte) cab address
|
||||
// * CV #29 - bit 5 indicates whether mobile decoder is using long or short cab address
|
||||
//
|
||||
// - CV #17 and CV #18 are combined into a single cab address
|
||||
// - three input boxes display the results of the short address, the long address,
|
||||
// - and whether of not the mobile decoder is using the long or short cab address
|
||||
//
|
||||
// ProgShortAddWriteButton - sends a DCC PROGRAMMING COMMAND to the DCC++ Base Station
|
||||
// that writes the short cab address specified in the first input box described above
|
||||
// to a mobile decoder on the Programming Track
|
||||
//
|
||||
// ProgLongAddWriteButton - sends a DCC PROGRAMMING COMMAND to the DCC++ Base Station
|
||||
// that writes the long cab address specified in the second input box described above
|
||||
// to a mobile decoder on the Programming Track
|
||||
//
|
||||
// ProgLongShortButton - sends a DCC PROGRAMMING COMMAND to the DCC++ Base Station
|
||||
// that indicates whether the mobile decoder on the Programming Track should use its
|
||||
// short cab address or long cab address
|
||||
//
|
||||
// The default configuration of DCC++ Controller defines a Programming Window that includes all of the above components
|
||||
//
|
||||
//
|
||||
// OpWriteButton - sends a DCC PROGRAMMING COMMAND to the DCC++ Base Station that writes a user-specified byte
|
||||
// or sets/clears a user-specified bit in a user-specified CV of a mobile decoder with a
|
||||
// user-specified cab address on the Main Operations Track
|
||||
// - uses one input box for specifiying the cab address and one for the CV
|
||||
// - when writing a full byte, uses 3 linked boxes for specifying the value in
|
||||
// either HEX, DECIMAL, or BINARY format
|
||||
// - when setting/clearing a bit, uses on input box to specify the bit number
|
||||
// - the default configuration of DCC++ Controller defines an Operation Programming Window that
|
||||
// includes all of these components
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: ProgWriteReadButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ProgWriteReadButton extends EllipseButton implements CallBack{
|
||||
InputBox progCVInput, progValueInput;
|
||||
|
||||
ProgWriteReadButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox progCVInput, InputBox progValueInput){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, progCVInput, progValueInput);
|
||||
}
|
||||
|
||||
ProgWriteReadButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox progCVInput, InputBox progValueInput){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT);
|
||||
this.progCVInput=progCVInput;
|
||||
this.progValueInput=progValueInput;
|
||||
callBacks.add(this);
|
||||
} // ProgrWriteReadButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
super.pressed();
|
||||
int cv=progCVInput.getIntValue();
|
||||
int val=progValueInput.getIntValue();
|
||||
if(cv<1 || cv>1024){
|
||||
msgBoxMain.setMessage("Error - CV must be in range 1-1024",color(255,30,30));
|
||||
} else if(bText=="WRITE"){
|
||||
aPort.write("<W"+cv+" "+val+" "+callBacks.indexOf(this)+" 1>");
|
||||
} else if(bText=="READ"){
|
||||
aPort.write("<R"+cv+" "+callBacks.indexOf(this)+" 0>");
|
||||
}
|
||||
} // pressed
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void execute(int n, String c){
|
||||
String[] cs = splitTokens(c);
|
||||
|
||||
int cv=int(cs[0]);
|
||||
int val=int(cs[1]);
|
||||
|
||||
progCVInput.setIntValue(cv);
|
||||
|
||||
if(val<0){
|
||||
msgBoxMain.setMessage(n==0?"Error - Read Failed":"Error - Write Failed",color(255,30,30));
|
||||
progHEXInput.resetValue();
|
||||
progBINInput.resetValue();
|
||||
progDECInput.resetValue();
|
||||
} else{
|
||||
msgBoxMain.setMessage(n==0?"Read Succeeded":"Write Succeeded",color(30,150,30));
|
||||
progHEXInput.setIntValue(val);
|
||||
progBINInput.setIntValue(val);
|
||||
progDECInput.setIntValue(val);
|
||||
}
|
||||
|
||||
} // execute
|
||||
|
||||
} // progWriteReadButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: ProgAddReadButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ProgAddReadButton extends EllipseButton implements CallBack{
|
||||
InputBox shortAddInput, longAddInput;
|
||||
MessageBox activeAddBox;
|
||||
int longAdd;
|
||||
|
||||
ProgAddReadButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox shortAddInput, InputBox longAddInput, MessageBox activeAddBox){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, shortAddInput, longAddInput, activeAddBox);
|
||||
}
|
||||
|
||||
ProgAddReadButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox shortAddInput, InputBox longAddInput, MessageBox activeAddBox){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT);
|
||||
this.shortAddInput=shortAddInput;
|
||||
this.longAddInput=longAddInput;
|
||||
this.activeAddBox=activeAddBox;
|
||||
callBacks.add(this);
|
||||
} // ProgAddReadButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
super.pressed();
|
||||
|
||||
aPort.write("<R1 "+callBacks.indexOf(this)+" 0>");
|
||||
|
||||
} // pressed
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void execute(int n, String c){
|
||||
String[] cs = splitTokens(c);
|
||||
|
||||
int cv=int(cs[0]);
|
||||
int val=int(cs[1]);
|
||||
|
||||
switch(cv){
|
||||
|
||||
case 1:
|
||||
if(val<0){
|
||||
msgBoxMain.setMessage("Error - Reading Short Address Failed",color(255,30,30));
|
||||
shortAddInput.resetValue();
|
||||
} else{
|
||||
shortAddInput.setIntValue(val);
|
||||
aPort.write("<R17 "+callBacks.indexOf(this)+" 0>");
|
||||
}
|
||||
break;
|
||||
|
||||
case 17:
|
||||
if(val<0){
|
||||
msgBoxMain.setMessage("Error - Reading First Byte of Long Address Failed",color(255,30,30));
|
||||
longAddInput.resetValue();
|
||||
} else{
|
||||
longAdd=(val&0x3F)*256;
|
||||
aPort.write("<R18 "+callBacks.indexOf(this)+" 0>");
|
||||
}
|
||||
break;
|
||||
|
||||
case 18:
|
||||
if(val<0){
|
||||
msgBoxMain.setMessage("Error - Reading Second Byte of Long Address Failed",color(255,30,30));
|
||||
longAddInput.resetValue();
|
||||
} else{
|
||||
longAdd+=val;
|
||||
longAddInput.setIntValue(longAdd);
|
||||
aPort.write("<R29 "+callBacks.indexOf(this)+" 0>");
|
||||
}
|
||||
break;
|
||||
|
||||
case 29:
|
||||
if(val<0){
|
||||
msgBoxMain.setMessage("Error - Reading Second Byte of Long Address Failed",color(255,30,30));
|
||||
activeAddBox.setMessage("?",color(200,50,50));
|
||||
} else{
|
||||
if((val&0x20)==0)
|
||||
activeAddBox.setMessage("SHORT",color(200,50,50));
|
||||
else
|
||||
activeAddBox.setMessage("LONG",color(200,50,50));
|
||||
msgBoxMain.setMessage("Reading Short and Long Addresses Succeeded",color(30,150,30));
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
} // execute
|
||||
|
||||
} // ProgAddReadButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: ProgShortAddWriteButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ProgShortAddWriteButton extends EllipseButton implements CallBack{
|
||||
InputBox addInput;
|
||||
|
||||
ProgShortAddWriteButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox addInput){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, addInput);
|
||||
}
|
||||
|
||||
ProgShortAddWriteButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox addInput){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT);
|
||||
this.addInput=addInput;
|
||||
callBacks.add(this);
|
||||
} // ProgAddReadButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
super.pressed();
|
||||
|
||||
int val=addInput.getIntValue();
|
||||
|
||||
if(val<1 || val>127){
|
||||
msgBoxMain.setMessage("Error - Short Address must be in range 1-127",color(255,30,30));
|
||||
} else {
|
||||
aPort.write("<W1"+" "+val+" "+callBacks.indexOf(this)+" 0>");
|
||||
}
|
||||
|
||||
} // pressed
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void execute(int n, String c){
|
||||
String[] cs = splitTokens(c);
|
||||
|
||||
int cv=int(cs[0]);
|
||||
int val=int(cs[1]);
|
||||
|
||||
if(val<0){
|
||||
msgBoxMain.setMessage("Error - Write Short Address Failed",color(255,30,30));
|
||||
addInput.resetValue();
|
||||
} else{
|
||||
msgBoxMain.setMessage("Write Short Address Succeeded",color(30,150,30));
|
||||
addInput.setIntValue(val);
|
||||
}
|
||||
|
||||
} // execute
|
||||
|
||||
} // ProgShortAddWriteButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: ProgLongAddWriteButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ProgLongAddWriteButton extends EllipseButton implements CallBack{
|
||||
InputBox addInput;
|
||||
int longAddIn, longAddOut;
|
||||
|
||||
ProgLongAddWriteButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox addInput){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, addInput);
|
||||
}
|
||||
|
||||
ProgLongAddWriteButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox addInput){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT);
|
||||
this.addInput=addInput;
|
||||
callBacks.add(this);
|
||||
} // ProgAddReadButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
super.pressed();
|
||||
|
||||
longAddIn=addInput.getIntValue();
|
||||
|
||||
if(longAddIn<0 || longAddIn>10239){
|
||||
msgBoxMain.setMessage("Error - Long Address must be in range 0-10239",color(255,30,30));
|
||||
} else {
|
||||
aPort.write("<W17"+" "+(longAddIn/256+192)+" "+callBacks.indexOf(this)+" 0>");
|
||||
}
|
||||
|
||||
} // pressed
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void execute(int n, String c){
|
||||
String[] cs = splitTokens(c);
|
||||
|
||||
int cv=int(cs[0]);
|
||||
int val=int(cs[1]);
|
||||
|
||||
switch(cv){
|
||||
|
||||
case 17:
|
||||
if(val<0){
|
||||
msgBoxMain.setMessage("Error - Writing First Byte of Long Address Failed",color(255,30,30));
|
||||
addInput.resetValue();
|
||||
} else{
|
||||
longAddOut=(val&0x3F)*256;
|
||||
aPort.write("<W18"+" "+(longAddIn%256)+" "+callBacks.indexOf(this)+" 0>");
|
||||
}
|
||||
break;
|
||||
|
||||
case 18:
|
||||
if(val<0){
|
||||
msgBoxMain.setMessage("Error - Writing Second Byte of Long Address Failed",color(255,30,30));
|
||||
addInput.resetValue();
|
||||
} else{
|
||||
msgBoxMain.setMessage("Write Long Address Succeeded",color(30,150,30));
|
||||
longAddOut+=val;
|
||||
addInput.setIntValue(longAddOut);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} // execute
|
||||
|
||||
} // ProgLongAddWriteButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: ProgLongShortButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ProgLongShortButton extends EllipseButton implements CallBack{
|
||||
MessageBox activeAddBox;
|
||||
|
||||
ProgLongShortButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, MessageBox activeAddBox){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, activeAddBox);
|
||||
}
|
||||
|
||||
ProgLongShortButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, MessageBox activeAddBox){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT);
|
||||
this.activeAddBox=activeAddBox;
|
||||
callBacks.add(this);
|
||||
} // ProgrWriteReadButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
super.pressed();
|
||||
|
||||
if(bText=="Long"){
|
||||
aPort.write("<B 29 5 1 "+callBacks.indexOf(this)+" 1>");
|
||||
} else if(bText=="Short"){
|
||||
aPort.write("<B 29 5 0 "+callBacks.indexOf(this)+" 0>");
|
||||
}
|
||||
|
||||
} // pressed
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void execute(int n, String c){
|
||||
String[] cs = splitTokens(c);
|
||||
|
||||
int val=int(cs[2]);
|
||||
|
||||
switch(val){
|
||||
|
||||
case -1:
|
||||
msgBoxMain.setMessage(n==1?"Error - Activating Long Address Failed":"Error - Activating Short Address Failed",color(255,30,30));
|
||||
activeAddBox.setMessage("?",color(200,50,50));
|
||||
break;
|
||||
|
||||
case 0:
|
||||
msgBoxMain.setMessage("Activating Short Address Succeeded",color(30,150,30));
|
||||
activeAddBox.setMessage("SHORT",color(200,50,50));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
msgBoxMain.setMessage("Activating Long Address Succeeded",color(30,150,30));
|
||||
activeAddBox.setMessage("LONG",color(200,50,50));
|
||||
break;
|
||||
}
|
||||
|
||||
} // execute
|
||||
|
||||
} // ProgLongShortButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: OpWriteButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class OpWriteButton extends EllipseButton{
|
||||
InputBox opCVInput, opValueInput;
|
||||
|
||||
OpWriteButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox opCVInput, InputBox opValueInput){
|
||||
this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, opCVInput, opValueInput);
|
||||
}
|
||||
|
||||
OpWriteButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox opCVInput, InputBox opValueInput){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT);
|
||||
this.opCVInput=opCVInput;
|
||||
this.opValueInput=opValueInput;
|
||||
} // OpWriteButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
super.pressed();
|
||||
int cab=opCabInput.getIntValue();
|
||||
int cv=opCVInput.getIntValue();
|
||||
int val=opValueInput.getIntValue();
|
||||
|
||||
if(cab<1 || cab>10239){
|
||||
msgBoxMain.setMessage("Error - Cab must be in range 1-10239",color(255,30,30));
|
||||
return;
|
||||
}
|
||||
if(cv<1 || cv>1024){
|
||||
msgBoxMain.setMessage("Error - CV must be in range 1-1024",color(255,30,30));
|
||||
return;
|
||||
}
|
||||
|
||||
if(bText=="WRITE"){
|
||||
aPort.write("<w"+cab+" "+cv+" "+val+" >");
|
||||
return;
|
||||
}
|
||||
|
||||
if(val>7){
|
||||
msgBoxMain.setMessage("Error - Bit must be in range 0-7",color(255,30,30));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(bText=="SET"){
|
||||
aPort.write("<b"+cab+" "+cv+" "+val+" 1>");
|
||||
} else if(bText=="CLEAR"){
|
||||
aPort.write("<b"+cab+" "+cv+" "+val+" 0>");
|
||||
}
|
||||
|
||||
} // pressed
|
||||
|
||||
} // OpWriteButton Class
|
||||
@@ -0,0 +1,247 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC++ CONTROLLER: Serial Components
|
||||
//
|
||||
// All classes and methods related to serial communication to and from
|
||||
// the DCC++ Base Station
|
||||
//
|
||||
// PortScanButton - function depends on button label as follows:
|
||||
//
|
||||
// "SCAN" - create a list all serial ports on the computer
|
||||
// ">" - scroll forward through the list
|
||||
// "<" - scroll backwards through the list
|
||||
// "CONNECT" - attempt to connect to a DCC++ Base Station
|
||||
//
|
||||
// - the default configuration of DCC++ Controller defines a
|
||||
// Serial Window that includes all of these components
|
||||
//
|
||||
// ArduinoPort - defines a generic port connection to the DCC++ Base Station
|
||||
// - extends Processing's normal Serial class by adding an
|
||||
// Ethernet or WiFi Client connection at port 2560 as well as
|
||||
// a "simulation" function so that DCC++ Controller can be run
|
||||
// in "emulator" mode without actually establishing a connection
|
||||
// to the DCC++ Base Station
|
||||
// - ideal for developing, testing, and demonstrating DCC++ Controller
|
||||
// without an Arduino
|
||||
// - also adds functionality that echos to a pre-specified text box all
|
||||
// text that is written to the DCC++ Base Station
|
||||
// - the default configuration of DCC++ Controller defines a
|
||||
// Diagnostic Window that includes this text box and is useful for
|
||||
// observing the exact commands DCC++ Controller sends to the
|
||||
// DCC++ Base Station
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DCC Component: PortScanButton
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class PortScanButton extends RectButton{
|
||||
boolean isComplete;
|
||||
|
||||
PortScanButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){
|
||||
super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT);
|
||||
} // AccessoryButton
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void pressed(){
|
||||
isComplete=false;
|
||||
super.pressed();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void scan(){
|
||||
String[] emulator = {"Emulator"};
|
||||
String[] serverList=splitTokens(serverListXML.getContent());
|
||||
|
||||
|
||||
aPort.portList=concat(emulator,Serial.list());
|
||||
aPort.portList=concat(aPort.portList,serverList);
|
||||
|
||||
aPort.displayedPort=0;
|
||||
portBox.setMessage(aPort.portList[aPort.displayedPort],aPort.portList[aPort.displayedPort].equals(arduinoPortXML.getContent())?color(50,150,50):color(50,50,200));
|
||||
portNumBox.setMessage("Port "+(aPort.displayedPort+1)+" of "+aPort.portList.length,color(50,50,50));
|
||||
|
||||
} // scan
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void turnOff(){
|
||||
String[] emulator = {"Emulator"};
|
||||
|
||||
if(isComplete==false){
|
||||
isComplete=true;
|
||||
return;
|
||||
}
|
||||
|
||||
super.turnOff();
|
||||
|
||||
if(bText=="SCAN"){
|
||||
scan();
|
||||
return;
|
||||
} // SCAN
|
||||
|
||||
if(bText==">" && aPort.portList!=null && aPort.portList.length>0){
|
||||
aPort.displayedPort=(aPort.displayedPort+1)%aPort.portList.length;
|
||||
portBox.setMessage(aPort.portList[aPort.displayedPort],aPort.portList[aPort.displayedPort].equals(arduinoPortXML.getContent())?color(50,150,50):color(50,50,200));
|
||||
portNumBox.setMessage("Port "+(aPort.displayedPort+1)+" of "+aPort.portList.length,color(50,50,50));
|
||||
return;
|
||||
} // >
|
||||
|
||||
if(bText=="<" && aPort.portList!=null && aPort.portList.length>0){
|
||||
if(--aPort.displayedPort<0)
|
||||
aPort.displayedPort=aPort.portList.length-1;
|
||||
portBox.setMessage(aPort.portList[aPort.displayedPort],aPort.portList[aPort.displayedPort].equals(arduinoPortXML.getContent())?color(50,150,50):color(50,50,200));
|
||||
portNumBox.setMessage("Port "+(aPort.displayedPort+1)+" of "+aPort.portList.length,color(50,50,50));
|
||||
return;
|
||||
} // <
|
||||
|
||||
if(bText=="CONNECT" && aPort.portList!=null && aPort.portList.length>0){
|
||||
arduinoPortXML.setContent(aPort.portList[aPort.displayedPort]);
|
||||
portBox.setMessage(aPort.portList[aPort.displayedPort],aPort.portList[aPort.displayedPort].equals(arduinoPortXML.getContent())?color(50,150,50):color(50,50,200));
|
||||
saveXML(dccStatusXML,STATUS_FILE);
|
||||
baseID=null;
|
||||
aPort.open(arduinoPortXML.getContent());
|
||||
return;
|
||||
} // <
|
||||
|
||||
} // pressed
|
||||
|
||||
} // PortScanButton Class
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ArduinoPort
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ArduinoPort{
|
||||
Serial port;
|
||||
Client client;
|
||||
String[] portList;
|
||||
int displayedPort;
|
||||
boolean emulate;
|
||||
String portName;
|
||||
int baud;
|
||||
|
||||
ArduinoPort(){
|
||||
emulate=false;
|
||||
port=null;
|
||||
client=null;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void write(String text){
|
||||
msgBoxDiagOut.setMessage(text,color(30,30,150));
|
||||
|
||||
if(emulate)
|
||||
simulate(text);
|
||||
else if(port!=null)
|
||||
port.write(text);
|
||||
else if(client!=null)
|
||||
client.write(text);
|
||||
|
||||
} // write
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void simulate(String text){
|
||||
String c = text.substring(2,text.length()-1);
|
||||
|
||||
switch(text.charAt(1)){
|
||||
|
||||
case 'c':
|
||||
if(powerButton.isOn)
|
||||
receivedString("<a150>");
|
||||
else
|
||||
receivedString("<a10>");
|
||||
break;
|
||||
|
||||
case '0':
|
||||
receivedString("<p0>");
|
||||
break;
|
||||
|
||||
case '1':
|
||||
receivedString("<p1>");
|
||||
break;
|
||||
|
||||
case 't':
|
||||
String[] s = splitTokens(c);
|
||||
if(int(s[2])==-1)
|
||||
s[2]="0";
|
||||
receivedString("<T"+s[0]+" "+s[2]+" "+s[3]+">");
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
String[] s1 = splitTokens(c);
|
||||
receivedString("<H"+s1[0]+" "+s1[1]+">");
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
String[] s2 = splitTokens(c);
|
||||
receivedString("<Z"+s2[0]+" "+s2[1]+">");
|
||||
break;
|
||||
|
||||
} //switch
|
||||
|
||||
} // simulate
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void open(String portName){
|
||||
int t;
|
||||
this.portName=portName;
|
||||
|
||||
emulate=false;
|
||||
|
||||
if(port!=null)
|
||||
port.stop();
|
||||
|
||||
if(client!=null)
|
||||
client.stop();
|
||||
|
||||
int[] n=int(splitTokens(portName,"."));
|
||||
|
||||
if(n.length==4 && n[0]>0 && n[0]<=255 && n[1]>=0 && n[1]<=255 && n[2]>=0 && n[2]<=255 && n[3]>=0 && n[3]<=255){
|
||||
client=new Client(Applet,portName,2560);
|
||||
if(client.ip()==null){
|
||||
msgBoxMain.setMessage("Can't connect to Server: "+portName,color(200,50,0));
|
||||
client=null;
|
||||
return;
|
||||
} else if(client!=null){
|
||||
msgBoxMain.setMessage("Waiting for Base Station at Server: "+client.ip(),color(200,50,0));
|
||||
client.write("<s>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(portName.equals("Emulator")){
|
||||
emulate=true;
|
||||
msgBoxMain.setMessage("Using Emulator to Simulate Arduino",color(50,50,200));
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
port=new Serial(Applet,portName,BASE_BAUD);
|
||||
port.bufferUntil('>');
|
||||
} catch(Exception e){
|
||||
msgBoxMain.setMessage("Serial Port Busy: "+portName,color(200,50,0));
|
||||
port=null;
|
||||
return;
|
||||
}
|
||||
|
||||
if(port.port==null){
|
||||
msgBoxMain.setMessage("Can't find Serial Port: "+portName,color(200,50,0));
|
||||
port=null;
|
||||
return;
|
||||
}
|
||||
|
||||
msgBoxMain.setMessage("Waiting for Base Station at Serial Port: "+portName,color(200,50,0));
|
||||
|
||||
t=millis();
|
||||
while(millis()-t<3000);
|
||||
port.write("<s>");
|
||||
|
||||
} // open()
|
||||
|
||||
} // Class ArduinoPort
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||