Improvements to WebUI
ln.build - build0.waldrennach.l--n.de build job pending Details

master
Harald Wolff 2020-12-22 23:34:05 +01:00
parent 9c054f3865
commit 1283d1c570
8 changed files with 362 additions and 121 deletions

View File

@ -36,8 +36,6 @@ namespace ln.ethercat.service
ethercatService.ECMaster.OnStateChange += (ECMaster ECMaster, ECSlaveState newState) => {
if (newState == ECSlaveState.OPERATIONAL)
{
ECMaster.DriveControllers[1].IgnoredByController = true;
if (ECMaster.GetSDOValue(1, 0x2012, 32, out SDOValue svEnableDrives))
{
svEnableDrives.SetValue((byte)0x00);
@ -52,7 +50,6 @@ namespace ln.ethercat.service
ethercatService.ECMaster.Controller.OnStateChanged += (controller, state) => {
Logging.Log(LogLevel.DEBUG, "ControllerState=={0}", state);
if (state == ControllerStates.NOTREADY)
{
if (controller.ECMaster.GetSDOValue(1, 0x2012, 32, out SDOValue svEnableDrives))
@ -60,9 +57,6 @@ namespace ln.ethercat.service
Logging.Log(LogLevel.DEBUG, "ControllerState=={0} powering up enable signals", state);
svEnableDrives.SetValue((byte)0x01);
}
} else if (state == ControllerStates.READY)
{
controller.ECMaster.DriveControllers[1].DriveMode = DriveMode.TORQUE;
}
};

View File

@ -1,11 +1,14 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using ln.ethercat.controller;
using ln.ethercat.controller.drives;
using ln.http.websocket;
using ln.json;
using ln.json.mapping;
using ln.logging;
using ln.type;
namespace ln.ethercat.service.api.v1
{
@ -48,6 +51,8 @@ namespace ln.ethercat.service.api.v1
.Add("State", controller.ControllerState.ToString())
.Add("Drives", jsonDriveControllers)
.Add("CycleCounter", controller.CycleCounter)
.Add("DisableRemoteWatchdog", controller.DisableRemoteWatchdog)
.Add("IgnoreRemoteInterface", controller.IgnoreRemoteInterface)
;
@ -100,12 +105,40 @@ namespace ln.ethercat.service.api.v1
public override void Received(string textMessage)
{
object target;
base.Received(textMessage);
JSONValue jsonTextValue = JSONParser.Parse(textMessage);
if (jsonTextValue is JSONObject jsonMessage)
{
string eventName = jsonMessage["event"].ToNative().ToString();
JSONObject jsonValue = jsonMessage["value"] as JSONObject;
switch (eventName)
{
case "set":
string propName = jsonValue["name"].ToNative().ToString();
object propValue = jsonValue["value"].ToNative();
target = Controller;
if (jsonValue.ContainsKey("drive"))
target = Controller.ECMaster.DriveControllers[(Int64)jsonValue["drive"].ToNative()];
PropertyInfo propertyInfo = target.GetType().GetProperty(propName);
propertyInfo.SetValue(target, Cast.To(propValue, propertyInfo.PropertyType));
break;
case "action":
string methodName = jsonValue["method"].ToNative().ToString();
JSONMapper.DefaultMapper.Deserialize(jsonValue["arguments"], out object[] arguments);
target = Controller;
if (jsonValue.ContainsKey("drive"))
target = Controller.ECMaster.DriveControllers[(Int64)jsonValue["drive"].ToNative()];
MethodInfo methodInfo = target.GetType().GetMethod(methodName);
methodInfo.Invoke(target,new object[0]);
break;
}
}
}

View File

@ -95,12 +95,46 @@ LN
template: src,
data: function(){
let d = { controller: {}, drive_controller: null };
let socket = $ECAPP.connectWebSocket("/api/v1/sockets/controller");
socket.onmessage = (evt)=>{
d.socket = $ECAPP.connectWebSocket("/api/v1/sockets/controller");
d.socket.onmessage = (evt)=>{
let json = JSON.parse(evt.data);
d.controller = json;
d.controller = json.value;
};
return d;
},
methods: {
drive_set: function(drive, propName, propValue){
let jsonMessage = {
event: "set",
value: {
drive: drive,
name: propName,
value: propValue
}
};
this.socket.send(JSON.stringify(jsonMessage));
},
ctrl_set: function(propName, propValue){
let jsonMessage = {
event: "set",
value: {
name: propName,
value: propValue
}
};
this.socket.send(JSON.stringify(jsonMessage));
},
ctrl_action: function(methodName, arguments){
arguments = arguments || [];
let jsonMessage = {
event: "action",
value: {
method: methodName,
arguments: arguments
}
};
this.socket.send(JSON.stringify(jsonMessage));
}
}
}
}]);

View File

@ -22,6 +22,7 @@ main, .flex {
}
main > *, .flex > * {
flex-basis: 100%;
flex-grow: 1;
margin: 0px 8px;
}
@ -29,9 +30,17 @@ main > *, .flex > * {
main > aside, .flex > .aside{
flex-basis: 25%;
flex-grow: 0;
min-width: 25%;
}
.group {
display: block;
border: 1px solid #ccc;
padding: 8px;
}
.group > fieldset {
border: none;
}
h2 {
border-bottom: 1px solid black;
@ -43,20 +52,41 @@ button, span {
font-style: normal;
text-decoration: none;
}
button.selected {
color: #10E010;
}
button.true {
color: #10E010;
}
button.false {
color: #B0B0B0;
}
fieldset {
position: relative;
display: flex;
}
fieldset > label {
flex-grow: 0;
}
fieldset > input, fieldset > .value {
fieldset > :first-child {
flex-grow: 1;
flex-basis: 50%;
}
fieldset > :nth-child(2) {
flex-grow: 1;
flex-basis: 40%;
}
fieldset > :last-child {
flex-grow: 1;
flex-basis: 25%;
}
fieldset.equal > * {
flex-basis: 50%;
margin: 4px;
}
.flex {
display: flex;
}
.panel {
padding: 6px;
}
@ -88,6 +118,24 @@ fieldset > input, fieldset > .value {
border-radius: 8px;;
background-color: #808080;
}
.state.large {
width: 240px;
}
.state.READY {
color: #108010;
}
.state.OPERATIONAL {
color: #10E010;
}
.state.ERROR, .state.FAULT {
color: white;
background-color: #C04040;
}
.state.BOOT, .state.INIT {
color: #000080;
}
.state.active {
background-color: #10E010;
@ -104,15 +152,10 @@ fieldset > input, fieldset > .value {
.state.active.orange {
background-color: #e0a810;
}
.OPERATIONAL {
background-color: #10E010;
}
.SAFE_OP {
.state.SAFE_OP {
background-color: #e0a810;
}
.PRE_OP {
.state.PRE_OP {
background-color: #B0B0B0;
}

View File

@ -1,32 +1,47 @@
<div>
<h1>Controller</h1>
<div class="panel flex">
<column>
<fieldset>
<label>Status</label>
<span class="state" :class="controller.State">{{ controller.State }}</span>
</fieldset>
<fieldset class="equal">
<button @click="ctrl_action('DisableDrives');">DISABLE</button>
<button @click="ctrl_action('EnableDrives');">ENABLE</button>
<button @click="ctrl_action('ClearFaults');">CLEAR FAULT</button>
</fieldset>
<fieldset class="equal">
<button @click="ctrl_set('DisableRemoteWatchdog', !controller.DisableRemoteWatchdog);" :class="controller.DisableRemoteWatchdog.toString()">DisableRemoteWatchdog</button>
<button @click="ctrl_set('IgnoreRemoteInterface', !controller.IgnoreRemoteInterface);" :class="controller.IgnoreRemoteInterface.toString()">IgnoreRemoteInterface</button>
</fieldset>
</column>
<column>
<div class="group"
v-for="drive,key in controller.Drives"
>
<fieldset>
<label>Drive {{ key }}</label>
<span class="state" :class="drive.DriveState">{{ drive.DriveState }}</span>
<span class="state large" :class="drive.OEMState">{{ drive.OEMState }}</span>
</fieldset>
<fieldset class="equal">
<label>Drive Mode</label>
<span class="value">{{ drive.DriveMode }}</span>
<label>Velocity</label>
<meter :value="Math.abs(drive.ActualSpeed)" min="0.0" max="1.5" low="0.8" high="1.0" optimum="0.7"></meter>
</fieldset>
<fieldset class="equal">
<label>TargetValue</label>
<input type="range" step="0.05" min="-1.0" max="1.0" v-model="drive.TargetValue" @input="drive_set(key, 'TargetValue', drive.TargetValue);">
<label>Torque</label>
<meter :value="Math.abs(drive.ActualTorque)" min="0.0" max="1.5" low="0.8" high="1.0" optimum="0.7"></meter>
</fieldset>
</div>
</column>
</div>
<p>
{{ controller }}
</p>
<div v-if="drive_controller">
<h2>Drive Controller</h2>
<button @click="drive_poweroff">Power Off</button>
<button @click="drive_poweron" >Power On</button>
<button @click="drive_disable" >Disable</button>
<button @click="drive_enable">Enable</button>
<button @click="drive_clearfault">Clear Fault</button>
<button @click="drive_drivemode('Position')">Mode: POS</button>
<button @click="drive_drivemode('Speed')">Mode: SPEED</button>
<button @click="drive_drivemode('Torque')">Mode: TORQUE</button>
<p>
{{ drive_controller }}
</p>
<div>
<fieldset>
<label>Target Torque:</label>
<input type="range" min="-1000" max="1000" v-model="targetTorque" id="targetTorque" @input="setProperty('TargetValue', targetTorque);">
<input type="number" v-model="targetTorque" @change="setProperty('TargetValue', targetTorque);">
<button @click="targetTorque = 0; setProperty('TargetValue', targetTorque);">ZERO</button>
</fieldset>
</div>
</div>
</div>

View File

@ -7,7 +7,6 @@ using ln.logging;
namespace ln.ethercat.controller
{
public delegate void ControllerLogicDelegate(Controller controller);
public delegate void ControllerStateChangeDelegate(Controller controller, ControllerStates newState);
@ -45,6 +44,8 @@ namespace ln.ethercat.controller
Thread threadWatchdog;
int wdogCounter;
public bool DisableRemoteWatchdog { get; set; }
int remoteWatchdogCounter;
public int WatchdogReset { get; set; } = 5;
public Controller(ECMaster ecMaster)
@ -67,7 +68,6 @@ namespace ln.ethercat.controller
foreach (DriveController driveController in ECMaster.DriveControllers)
driveController.Initialize();
}
void UpdateControllerState()
@ -172,15 +172,17 @@ namespace ln.ethercat.controller
}
}
public void RemoteTriggerWatchdog() => remoteWatchdogCounter = 5;
public void RemoteUpdateTarget(int drive, double targetValue)
{
{
if (!IgnoreRemoteInterface)
{
targetValue = Math.Clamp(targetValue, 0, 1);
targetValue = Math.Clamp(targetValue, -1, 1);
if (drive < 0)
throw new ArgumentOutOfRangeException(nameof(targetValue));
if (drive > ECMaster.DriveControllers.Length)
if (drive >= ECMaster.DriveControllers.Length)
{
Logging.Log(LogLevel.WARNING, "Controller: RemoteUpdateTarget(): trying to update non-existent drive {0}", drive);
return;
@ -217,7 +219,19 @@ namespace ln.ethercat.controller
try
{
Thread.Sleep(TimeSpan.FromSeconds(ControllerLoopInterval));
if ((wdogCounter--) < 0)
if (wdogCounter > 0)
{
wdogCounter--;
} else
{
ChangeState(ControllerStates.FAULT);
}
if (remoteWatchdogCounter > 0)
{
remoteWatchdogCounter--;
} else if (!DisableRemoteWatchdog)
{
ChangeState(ControllerStates.FAULT);
}

View File

@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using ln.logging;
namespace ln.ethercat.controller.drives
@ -42,6 +43,8 @@ namespace ln.ethercat.controller.drives
SDOValue svModesOfOperation;
SDOValue svModesOfOperationDisplay;
SDOValue svActualCurrent;
SDOValue svVLTargetVelocity;
SDOValue svVLActualVelocity;
SDOValue svMaxSpeed;
@ -62,7 +65,9 @@ namespace ln.ethercat.controller.drives
new PDOMappingRequest(slave, 0x6077, 0, false),
new PDOMappingRequest(slave, 0x6060, 0, true),
new PDOMappingRequest(slave, 0x6061, 0, false),
new PDOMappingRequest(slave, 0x6078, 0, false)
new PDOMappingRequest(slave, 0x6078, 0, false),
new PDOMappingRequest(slave, 0x6042, 0, true),
new PDOMappingRequest(slave, 0x6044, 0, false),
};
ecMaster.RequestPDOMapping(mappingRequests);
}
@ -82,68 +87,81 @@ namespace ln.ethercat.controller.drives
ECMaster.GetSDOValue(Slave, 0x6060, 0, out svModesOfOperation) &&
ECMaster.GetSDOValue(Slave, 0x6061, 0, out svModesOfOperationDisplay) &&
ECMaster.GetSDOValue(Slave, 0x6080, 0, out svMaxSpeed) &&
ECMaster.GetSDOValue(Slave, 0x6044, 0, out svVLActualVelocity) &&
ECMaster.GetSDOValue(Slave, 0x6042, 0, out svVLTargetVelocity) &&
ECMaster.GetSDOValue(Slave, 0x6078, 0, out svActualCurrent)
))
throw new ArgumentOutOfRangeException("CIA402Controller could not retrieve SDOvalues for CiA402 profile");
MotorMaxSpeed = svMaxSpeed.GetValue<UInt32>();
}
public override void UpdateStates() {
CIA402State = GetCIA402State();
}
public override void UpdateDrive() {
if (CIA402State != CIA402TargetState)
public override void UpdateDrive()
{
if (CIA402TargetState != CIA402States.UNDEFINED)
{
switch (CIA402State)
if (CIA402State != CIA402TargetState)
{
case CIA402States.NOT_READY_TO_SWITCH_ON:
svControlWord.SetValue((ushort)0x0000);
ModeOfOperation = CIA402ModesOfOperation.CYCLIC_SYNC_TORQUE;
break;
case CIA402States.SWITCH_ON_DISABLED:
svControlWord.SetValue((ushort)0x0006);
break;
case CIA402States.READY_TO_SWITCH_ON:
switch (CIA402TargetState)
{
case CIA402States.SWITCH_ON_DISABLED:
svControlWord.SetValue((ushort)0x0000);
break;
case CIA402States.SWITCHED_ON:
case CIA402States.OPERATION_ENABLED:
svControlWord.SetValue((ushort)0x0007);
break;
}
break;
case CIA402States.SWITCHED_ON:
switch (CIA402TargetState)
{
case CIA402States.SWITCH_ON_DISABLED:
case CIA402States.READY_TO_SWITCH_ON:
svControlWord.SetValue((ushort)0x0006);
break;
case CIA402States.OPERATION_ENABLED:
svControlWord.SetValue((ushort)0x000F);
break;
}
break;
case CIA402States.OPERATION_ENABLED:
switch (CIA402TargetState)
{
case CIA402States.SWITCH_ON_DISABLED:
case CIA402States.READY_TO_SWITCH_ON:
svControlWord.SetValue((ushort)0x0006);
break;
case CIA402States.SWITCHED_ON:
svControlWord.SetValue((ushort)0x0007);
break;
}
break;
switch (CIA402State)
{
case CIA402States.FAULT:
case CIA402States.FAULT_REACTION_ACTIVE:
CIA402TargetState = CIA402States.UNDEFINED;
break;
case CIA402States.NOT_READY_TO_SWITCH_ON:
svControlWord.SetValue((ushort)0x0000);
ModeOfOperation = CIA402ModesOfOperation.CYCLIC_SYNC_TORQUE;
break;
case CIA402States.SWITCH_ON_DISABLED:
svControlWord.SetValue((ushort)0x0006);
break;
case CIA402States.READY_TO_SWITCH_ON:
switch (CIA402TargetState)
{
case CIA402States.SWITCH_ON_DISABLED:
svControlWord.SetValue((ushort)0x0000);
break;
case CIA402States.SWITCHED_ON:
case CIA402States.OPERATION_ENABLED:
svControlWord.SetValue((ushort)0x0007);
break;
}
break;
case CIA402States.SWITCHED_ON:
switch (CIA402TargetState)
{
case CIA402States.SWITCH_ON_DISABLED:
case CIA402States.READY_TO_SWITCH_ON:
svControlWord.SetValue((ushort)0x0006);
break;
case CIA402States.OPERATION_ENABLED:
svControlWord.SetValue((ushort)0x000F);
break;
}
break;
case CIA402States.OPERATION_ENABLED:
switch (CIA402TargetState)
{
case CIA402States.SWITCH_ON_DISABLED:
case CIA402States.READY_TO_SWITCH_ON:
svControlWord.SetValue((ushort)0x0006);
break;
case CIA402States.SWITCHED_ON:
svControlWord.SetValue((ushort)0x0007);
break;
}
break;
}
} else {
CIA402TargetState = CIA402States.UNDEFINED;
}
}
if (DriveMode == DriveMode.UNDEFINED)
DriveMode = DriveMode.TORQUE;
DriveMode = DriveMode.SPEED;
}
public override DriveStates DriveState
@ -182,6 +200,7 @@ namespace ln.ethercat.controller.drives
case CIA402ModesOfOperation.CYCLIC_SYNC_POSITION:
return DriveMode.POSITION;
case CIA402ModesOfOperation.CYCLIC_SYNC_VELOCITY:
case CIA402ModesOfOperation.VL_VELOCITY_MODE:
return DriveMode.SPEED;
case CIA402ModesOfOperation.CYCLIC_SYNC_TORQUE:
return DriveMode.TORQUE;
@ -196,7 +215,7 @@ namespace ln.ethercat.controller.drives
ModeOfOperation = CIA402ModesOfOperation.CYCLIC_SYNC_POSITION;
break;
case DriveMode.SPEED:
ModeOfOperation = CIA402ModesOfOperation.CYCLIC_SYNC_VELOCITY;
ModeOfOperation = CIA402ModesOfOperation.VL_VELOCITY_MODE;
break;
case DriveMode.TORQUE:
ModeOfOperation = CIA402ModesOfOperation.CYCLIC_SYNC_TORQUE;
@ -214,11 +233,19 @@ namespace ln.ethercat.controller.drives
public CIA402ModesOfOperation ModeOfOperation {
get => (ECMaster.EthercatState==ECSlaveState.OPERATIONAL) ? (CIA402ModesOfOperation)(svModesOfOperationDisplay?.GetValue<sbyte>() ?? 0) : CIA402ModesOfOperation.NO_MODE_CHANGE;
set => svModesOfOperation.SetValue((sbyte)value);
set {
svModesOfOperation.SetValue((sbyte)value);
}
}
public override double ActualPosition => (svActualPosition?.GetValue<int>() ?? 0) ;
public override double ActualSpeed => (double)(svActualSpeed?.GetValue<int>() ?? 0) / (1000.0 * MotorMaxSpeed);
public override double ActualSpeed {
get {
if (ModeOfOperation == CIA402ModesOfOperation.CYCLIC_SYNC_VELOCITY)
return (double)(svActualSpeed?.GetValue<int>() ?? 0) / (1000.0 * MotorMaxSpeed);
return (double)(svVLActualVelocity?.GetValue<short>() ?? 0) / MotorMaxSpeed;
}
}
public override double ActualTorque => (double)(svActualTorque?.GetValue<short>() ?? 0) / 1000.0;
public override double ActualLoad => (double)(svActualCurrent?.GetValue<short>() ?? 0) / 1000.0;
@ -228,8 +255,16 @@ namespace ln.ethercat.controller.drives
}
public override double TargetSpeed {
get => (double)svTargetSpeed.GetValue<int>() / (1000.0 * MotorMaxSpeed);
set => svTargetSpeed.SetValue((int)(value * (1000.0 * MotorMaxSpeed)));
get {
if (ModeOfOperation == CIA402ModesOfOperation.CYCLIC_SYNC_VELOCITY)
return (double)svTargetSpeed.GetValue<int>() / (1000.0 * MotorMaxSpeed);
return (double)(svVLTargetVelocity?.GetValue<short>() ?? 0) / MotorMaxSpeed;
}
set {
if (ModeOfOperation == CIA402ModesOfOperation.CYCLIC_SYNC_VELOCITY)
svTargetSpeed.SetValue((int)(value * (1000.0 * MotorMaxSpeed)));
svVLTargetVelocity.SetValue((short)(value * MotorMaxSpeed));
}
}
public override double TargetTorque {
get => (double)svTargetTorque.GetValue<short>() / 1000.0;

View File

@ -2,11 +2,8 @@
using System;
using System.IO.Ports;
using System.Net.Http.Headers;
using System.Text;
using System.Threading;
using ln.logging;
using ln.type;
using ln.ethercat.controller.drives;
namespace ln.ethercat.controller.remote
{
@ -33,15 +30,29 @@ namespace ln.ethercat.controller.remote
LOAD100 = (1<<15),
ALL = -1
}
public enum FeederOperation
{
NONE,
LEFT,
RIGHT,
TORQUE
}
public class StupidSerialRemote : ControllerRemote
{
public string SerialPortName { get; }
public FeederOperation FeederOperation { get; set; }
SerialPort serialPort;
bool stopReceiverThread;
Thread threadReceiver;
public double MainTarget { get; set; }
public double FeedTarget { get; set; }
public StupidSerialRemote(Controller controller)
:this(controller, SerialPort.GetPortNames()[0]){}
public StupidSerialRemote(Controller controller, string serialDevice)
@ -77,12 +88,50 @@ namespace ln.ethercat.controller.remote
if (Controller.ECMaster.DriveControllers[0].ActualLoad >= 1)
leds |= StupidLEDs.LOAD100;
SetLEDs(leds);
Controller.RemoteUpdateTarget(0, MainTarget);
switch (FeederOperation)
{
case FeederOperation.NONE:
Controller.RemoteUpdateTarget(1, 0);
break;
case FeederOperation.LEFT:
if (Controller.ECMaster.DriveControllers[1].DriveMode == DriveMode.SPEED)
{
Controller.RemoteUpdateTarget(1, FeedTarget);
} else {
Controller.ECMaster.DriveControllers[1].DriveMode = DriveMode.SPEED;
Controller.RemoteUpdateTarget(1, 0);
}
break;
case FeederOperation.RIGHT:
if (Controller.ECMaster.DriveControllers[1].DriveMode == DriveMode.SPEED)
{
Controller.RemoteUpdateTarget(1, -FeedTarget);
} else {
Controller.ECMaster.DriveControllers[1].DriveMode = DriveMode.SPEED;
Controller.RemoteUpdateTarget(1, 0);
}
break;
case FeederOperation.TORQUE:
if (Controller.ECMaster.DriveControllers[1].DriveMode == DriveMode.TORQUE)
{
Controller.RemoteUpdateTarget(1, FeedTarget);
} else {
Controller.ECMaster.DriveControllers[1].DriveMode = DriveMode.TORQUE;
Controller.RemoteUpdateTarget(1, 0);
}
break;
}
break;
case ControllerStates.ENABLING:
break;
case ControllerStates.DISABLING:
break;
}
}
void Receiver()
@ -92,18 +141,25 @@ namespace ln.ethercat.controller.remote
string rxLine = serialPort.ReadLine();
//Logging.Log(LogLevel.DEBUGDETAIL, rxLine);
if (rxLine.Length >= 6)
{
ushort av = ushort.Parse(rxLine.Substring(2,4), System.Globalization.NumberStyles.HexNumber);
{
ushort av = 0;
ushort.TryParse(rxLine.Substring(2,4), System.Globalization.NumberStyles.HexNumber, null, out av);
switch (rxLine[0])
{
case 'A':
int drive = rxLine[1] - '0';
if (drive == 0) drive = 1;
else if (drive == 1) drive = 2;
else if (drive == 2) drive = 0;
double rel = (double)av / 65535;
if ((drive >= 0) && (drive < Controller.ECMaster.DriveControllers.Length))
Controller.RemoteUpdateTarget(drive, rel);
switch (drive)
{
case 0:
FeedTarget = rel;
break;
case 2:
MainTarget = rel;
break;
}
Controller.RemoteTriggerWatchdog();
break;
case 'B':
switch (rxLine[1])
@ -115,19 +171,36 @@ namespace ln.ethercat.controller.remote
if (Controller.ControllerState == ControllerStates.FAULT)
Controller.RemoteAction(CRActions.CLEARFAULT);
else if (Controller.ControllerState == ControllerStates.READY)
{
Controller.ECMaster.DriveControllers[0].DriveMode = DriveMode.SPEED;
Controller.RemoteAction(CRActions.ENABLE);
}
break;
case 1:
Controller.RemoteAction(CRActions.DISABLE);
break;
}
break;
case 'D':
switch (av)
{
case 3:
// ToDo: Feeder left
FeederOperation = FeederOperation.LEFT;
break;
case 4:
// ToDo: Feeder right
FeederOperation = FeederOperation.RIGHT;
break;
case 14:
// ToDo: cycle drilling
FeederOperation = FeederOperation.TORQUE;
break; }
break;
case 'U':
switch (av)
{
case 3:
case 4:
case 14:
FeederOperation = FeederOperation.NONE;
break;
}
break;