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) => { ethercatService.ECMaster.OnStateChange += (ECMaster ECMaster, ECSlaveState newState) => {
if (newState == ECSlaveState.OPERATIONAL) if (newState == ECSlaveState.OPERATIONAL)
{ {
ECMaster.DriveControllers[1].IgnoredByController = true;
if (ECMaster.GetSDOValue(1, 0x2012, 32, out SDOValue svEnableDrives)) if (ECMaster.GetSDOValue(1, 0x2012, 32, out SDOValue svEnableDrives))
{ {
svEnableDrives.SetValue((byte)0x00); svEnableDrives.SetValue((byte)0x00);
@ -52,7 +50,6 @@ namespace ln.ethercat.service
ethercatService.ECMaster.Controller.OnStateChanged += (controller, state) => { ethercatService.ECMaster.Controller.OnStateChanged += (controller, state) => {
Logging.Log(LogLevel.DEBUG, "ControllerState=={0}", state); Logging.Log(LogLevel.DEBUG, "ControllerState=={0}", state);
if (state == ControllerStates.NOTREADY) if (state == ControllerStates.NOTREADY)
{ {
if (controller.ECMaster.GetSDOValue(1, 0x2012, 32, out SDOValue svEnableDrives)) 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); Logging.Log(LogLevel.DEBUG, "ControllerState=={0} powering up enable signals", state);
svEnableDrives.SetValue((byte)0x01); 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;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using ln.ethercat.controller; using ln.ethercat.controller;
using ln.ethercat.controller.drives; using ln.ethercat.controller.drives;
using ln.http.websocket; using ln.http.websocket;
using ln.json; using ln.json;
using ln.json.mapping;
using ln.logging; using ln.logging;
using ln.type;
namespace ln.ethercat.service.api.v1 namespace ln.ethercat.service.api.v1
{ {
@ -48,6 +51,8 @@ namespace ln.ethercat.service.api.v1
.Add("State", controller.ControllerState.ToString()) .Add("State", controller.ControllerState.ToString())
.Add("Drives", jsonDriveControllers) .Add("Drives", jsonDriveControllers)
.Add("CycleCounter", controller.CycleCounter) .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) public override void Received(string textMessage)
{ {
object target;
base.Received(textMessage); base.Received(textMessage);
JSONValue jsonTextValue = JSONParser.Parse(textMessage); JSONValue jsonTextValue = JSONParser.Parse(textMessage);
if (jsonTextValue is JSONObject jsonMessage) if (jsonTextValue is JSONObject jsonMessage)
{ {
string eventName = jsonMessage["event"].ToNative().ToString(); 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, template: src,
data: function(){ data: function(){
let d = { controller: {}, drive_controller: null }; let d = { controller: {}, drive_controller: null };
let socket = $ECAPP.connectWebSocket("/api/v1/sockets/controller"); d.socket = $ECAPP.connectWebSocket("/api/v1/sockets/controller");
socket.onmessage = (evt)=>{ d.socket.onmessage = (evt)=>{
let json = JSON.parse(evt.data); let json = JSON.parse(evt.data);
d.controller = json; d.controller = json.value;
}; };
return d; 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 > * { main > *, .flex > * {
flex-basis: 100%;
flex-grow: 1; flex-grow: 1;
margin: 0px 8px; margin: 0px 8px;
} }
@ -29,9 +30,17 @@ main > *, .flex > * {
main > aside, .flex > .aside{ main > aside, .flex > .aside{
flex-basis: 25%; flex-basis: 25%;
flex-grow: 0; flex-grow: 0;
min-width: 25%;
} }
.group {
display: block;
border: 1px solid #ccc;
padding: 8px;
}
.group > fieldset {
border: none;
}
h2 { h2 {
border-bottom: 1px solid black; border-bottom: 1px solid black;
@ -43,20 +52,41 @@ button, span {
font-style: normal; font-style: normal;
text-decoration: none; text-decoration: none;
} }
button.selected {
color: #10E010;
}
button.true {
color: #10E010;
}
button.false {
color: #B0B0B0;
}
fieldset { fieldset {
position: relative; position: relative;
display: flex; display: flex;
} }
fieldset > label { fieldset > :first-child {
flex-grow: 0;
}
fieldset > input, fieldset > .value {
flex-grow: 1; 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 { .panel {
padding: 6px; padding: 6px;
} }
@ -88,6 +118,24 @@ fieldset > input, fieldset > .value {
border-radius: 8px;; border-radius: 8px;;
background-color: #808080; 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 { .state.active {
background-color: #10E010; background-color: #10E010;
@ -104,15 +152,10 @@ fieldset > input, fieldset > .value {
.state.active.orange { .state.active.orange {
background-color: #e0a810; background-color: #e0a810;
} }
.state.SAFE_OP {
.OPERATIONAL {
background-color: #10E010;
}
.SAFE_OP {
background-color: #e0a810; background-color: #e0a810;
} }
.PRE_OP { .state.PRE_OP {
background-color: #B0B0B0; background-color: #B0B0B0;
} }

View File

@ -1,32 +1,47 @@
<div> <div>
<h1>Controller</h1> <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> <p>
{{ controller }} {{ controller }}
</p> </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> </div>

View File

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

View File

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

View File

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