From 9c054f3865670bb305d203276a13734640dc3989 Mon Sep 17 00:00:00 2001 From: Harald Wolff Date: Mon, 21 Dec 2020 23:02:06 +0100 Subject: [PATCH] Still early alpha, controller works, stupidremote working --- libecmbind/ecmbind.h | 2 +- libecmbind/libecmbind.c | 12 + libecmbind/libecmbind_descriptors.c | 4 +- ln.ethercat.service/EthercatService.cs | 14 +- .../MainAxFeederControllerLogic.cs | 63 +++ ln.ethercat.service/Program.cs | 46 ++ .../api/v1/ControllerApiController.cs | 134 ++--- .../api/v1/ControllerWebSocket.cs | 115 +++++ .../api/v1/EthercatApiController.cs | 15 - .../api/v1/EthercatWebSocket.cs | 67 ++- ln.ethercat.service/www/html/spa.html | 6 +- ln.ethercat.service/www/static/css/spa.css | 6 +- ln.ethercat.service/www/static/js/ecapp.js | 6 + ln.ethercat.service/www/static/js/pdopanel.js | 18 +- ln.ethercat.service/www/static/js/sdopanel.js | 22 +- .../www/static/js/sdoselect.js | 19 +- .../www/static/pages/controller.html | 11 +- ln.ethercat.service/www/static/pages/pdo.html | 4 +- .../www/static/pages/slave.html | 5 +- ln.ethercat/ECDataTypeConverter.cs | 2 +- ln.ethercat/ECMBind.cs | 8 +- ln.ethercat/ECMaster.cs | 465 +++++++++++++----- ln.ethercat/ECSlave.cs | 10 +- ln.ethercat/controller/Controller.cs | 244 ++++++--- ln.ethercat/controller/ControllerRemote.cs | 105 ++++ .../controller/drives/CIA402Controller.cs | 162 +++--- .../controller/drives/DriveController.cs | 50 +- .../controller/remote/StupidSerialRemote.cs | 171 +++++++ ln.ethercat/lib/libecmbind.so | Bin 179424 -> 179512 bytes ln.ethercat/ln.ethercat.csproj | 1 + 30 files changed, 1387 insertions(+), 400 deletions(-) create mode 100644 ln.ethercat.service/MainAxFeederControllerLogic.cs create mode 100644 ln.ethercat.service/api/v1/ControllerWebSocket.cs create mode 100644 ln.ethercat/controller/ControllerRemote.cs create mode 100644 ln.ethercat/controller/remote/StupidSerialRemote.cs diff --git a/libecmbind/ecmbind.h b/libecmbind/ecmbind.h index 77a8087..641f6e1 100644 --- a/libecmbind/ecmbind.h +++ b/libecmbind/ecmbind.h @@ -27,7 +27,7 @@ typedef struct { } dto_servicedescriptor_t; typedef void (*cb_enum_indeces_t)(int slave, int index); -typedef void (*cb_enum_sdo_descriptors_t)(int slave, int index, uint16_t dataType, uint16_t objectCode, int sub, char *name); +typedef void (*cb_enum_sdo_descriptors_t)(int slave, int index, uint16_t dataType, uint16_t objectCode, int sub, ushort bitlength, char *name); typedef void (*cb_enum_pdo)(uint16_t slave, uint16_t index, char subindex, int addr_offset, int addr_bit, int bitlength); extern ecd_pdo_entry_t ecd_pdo_map[1024]; diff --git a/libecmbind/libecmbind.c b/libecmbind/libecmbind.c index 7029373..2b0ca6a 100644 --- a/libecmbind/libecmbind.c +++ b/libecmbind/libecmbind.c @@ -181,6 +181,18 @@ int ecmbind_sdo_write(int slave, int index, int subindex, char* buffer, int size return ec_SDOwrite(slave, index, subindex, FALSE, size, buffer, 250000L); } +int ecmbind_sdo_read_ca(int slave, int index, int subindex, char* buffer, int size) +{ + int wkc = ec_SDOread(slave, index, subindex, TRUE, &size, buffer, 250000L); + if (wkc <= 0) + return wkc; + return size; +} +int ecmbind_sdo_write_ca(int slave, int index, int subindex, char* buffer, int size) +{ + return ec_SDOwrite(slave, index, subindex, TRUE, size, buffer, 250000L); +} + /* typedef void (*cb_enum_pdo)(int slave, int index, int subindex, int addr_offset, int addr_bit, int bitlength); */ diff --git a/libecmbind/libecmbind_descriptors.c b/libecmbind/libecmbind_descriptors.c index 34a0ca7..fa588a4 100644 --- a/libecmbind/libecmbind_descriptors.c +++ b/libecmbind/libecmbind_descriptors.c @@ -189,7 +189,7 @@ int ecmbind_read_objectdescription(int Slave, int index, cb_enum_sdo_descriptors strncpy(temp , (char *)&aSDOp->bdata[6], n); temp[n] = 0x00; - cb(Slave, index, etohs(aSDOp->wdata[1]), aSDOp->bdata[5], aSDOp->bdata[4], temp); + cb(Slave, index, etohs(aSDOp->wdata[1]), aSDOp->bdata[5], aSDOp->bdata[4], 0, temp); } /* got unexpected response from slave */ else @@ -265,7 +265,7 @@ int ecmbind_read_objectdescription_entry(uint16_t slave, uint16_t index, uint16_ strncpy(temp , (char *)&aSDOp->wdata[5], n); temp[n] = 0x00; /* string terminator */ - cb(slave, index, (ushort)etohs(aSDOp->wdata[2]), 0, sub, temp); + cb(slave, index, (ushort)etohs(aSDOp->wdata[2]), 0, sub, (ushort)etohs(aSDOp->wdata[3]), temp); /* pOElist->ValueInfo[sub] = aSDOp->bdata[3]; diff --git a/ln.ethercat.service/EthercatService.cs b/ln.ethercat.service/EthercatService.cs index e32dc06..feca0ee 100644 --- a/ln.ethercat.service/EthercatService.cs +++ b/ln.ethercat.service/EthercatService.cs @@ -17,8 +17,6 @@ namespace ln.ethercat.service { public ECMaster ECMaster { get; private set; } - public Controller Controller { get; private set; } - [StaticArgument(Option = 'i', LongOption = "interface")] public string EthercatInterfaceName { get; set; } @@ -72,14 +70,9 @@ namespace ln.ethercat.service httpServer = new HTTPServer(httpLoggingRouter); httpServer.AddEndpoint(new Endpoint(IPv6.ANY, 7676)); - Controller = new Controller(); - Controller.Add(new CIA402Controller(ECMaster, 1)); - Controller.Add(new CIA402Controller(ECMaster, 2)); - - controllerApiController = new ControllerApiController(Controller); + controllerApiController = new ControllerApiController(ECMaster.Controller); httpRouter.AddSimpleRoute("/api/v1/*", controllerApiController); - timerWebsockets = new System.Timers.Timer(250); timerWebsockets.Elapsed += WebSocketTimerMethod; } @@ -97,6 +90,7 @@ namespace ln.ethercat.service { try{ EthercatWebSocket.SendProcessData(ECMaster); + ControllerWebSocket.SendUpdates(ECMaster.Controller); } catch (Exception ex) { Logging.Log(ex); @@ -108,10 +102,10 @@ namespace ln.ethercat.service switch (newState) { case ECSlaveState.OPERATIONAL: - Controller.Start(); + //Controller.Start(); break; default: - Controller.Stop(); + //Controller.Stop(); break; } } diff --git a/ln.ethercat.service/MainAxFeederControllerLogic.cs b/ln.ethercat.service/MainAxFeederControllerLogic.cs new file mode 100644 index 0000000..7393ebe --- /dev/null +++ b/ln.ethercat.service/MainAxFeederControllerLogic.cs @@ -0,0 +1,63 @@ + +using System; +using System.IO; +using ln.ethercat.controller; +using ln.json; +using ln.json.mapping; + +namespace ln.ethercat.service +{ + + public class MainAxFeederControllerLogic + { + public MyParameters Parameters { get; set; } + + ECMaster ECMaster; + + SDOValue svRelais; + SDOValue svEnable; + + public MainAxFeederControllerLogic(ECMaster ecMaster) + { + ECMaster = ecMaster; + + Parameters = new MyParameters(); + if (File.Exists("mafcl.json")) + { + JSONValue configValue = JSONParser.ParseFile("mafcl.json"); + JSONMapper.DefaultMapper.Apply(configValue as JSONObject, Parameters); + } + } + + public void Save() + { + if (JSONMapper.DefaultMapper.Serialize(Parameters, out JSONValue configObject)) + { + using (StreamWriter sw = new StreamWriter("mafcl.json")) + { + sw.Write(configObject.ToString()); + sw.Flush(); + } + } + } + + public void Initialize() + { + if (!( + ECMaster.GetSDOValue(1, 0x2012, 31, out svRelais) && + ECMaster.GetSDOValue(1, 0x2012, 32, out svEnable) + )) + throw new Exception("could not retrieve needed SDOValues"); + } + + public void ControllerLogic(Controller contreller) + { + } + + + public class MyParameters { + + } + + } +} \ No newline at end of file diff --git a/ln.ethercat.service/Program.cs b/ln.ethercat.service/Program.cs index 6c74019..c3e5be2 100644 --- a/ln.ethercat.service/Program.cs +++ b/ln.ethercat.service/Program.cs @@ -1,9 +1,11 @@ using System; +using System.Data; using System.Text; using System.Threading; using ln.application; using ln.ethercat.controller; using ln.ethercat.controller.drives; +using ln.ethercat.controller.remote; using ln.logging; using ln.type; @@ -11,6 +13,9 @@ namespace ln.ethercat.service { class Program { + [StaticArgument(LongOption = "serial-remote")] + static string SerialRemotePort { get; set; } + static void Main(string[] args) { Logging.Log(LogLevel.INFO, ".NET EtherCAT service host"); @@ -23,11 +28,52 @@ namespace ln.ethercat.service EthercatService ethercatService = new EthercatService(args[0]); ArgumentContainer argumentContainer = new ArgumentContainer(); + argumentContainer.AddStaticOptions(); argumentContainer.AddOptions(ethercatService); argumentContainer.Parse(ref args); ethercatService.Initialize(); + 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); + } + + } else if (newState == ECSlaveState.PRE_OP) + { + ECMaster.RequestPDOMapping(1, 0x2012, 31, true); + ECMaster.RequestPDOMapping(1, 0x2012, 32, true); + } + }; + + 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)) + { + 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; + } + }; + ethercatService.Start(); + + + if (SerialRemotePort != null) + { + StupidSerialRemote stupidSerialRemote = new StupidSerialRemote(ethercatService.ECMaster.Controller, SerialRemotePort); + stupidSerialRemote.Start(); + } } } } diff --git a/ln.ethercat.service/api/v1/ControllerApiController.cs b/ln.ethercat.service/api/v1/ControllerApiController.cs index 722e06f..1e157ea 100644 --- a/ln.ethercat.service/api/v1/ControllerApiController.cs +++ b/ln.ethercat.service/api/v1/ControllerApiController.cs @@ -1,5 +1,3 @@ - - using System; using System.Globalization; using System.Linq; @@ -30,93 +28,61 @@ namespace ln.ethercat.service.api.v1 [GET("/sockets/controller")] - public HttpResponse GetControllerSocket() - { - Timer timer = new Timer(250); - JSONWebSocketResponse websocket = new JSONWebSocketResponse(); - websocket.OnWebSocketStateChanged += (Socket, newstate) => { - if (newstate == WebSocketState.CLOSED) - { - timer.Stop(); - timer.Dispose(); - } - }; + public HttpResponse GetControllerSocket() => new ControllerWebSocket(Controller); - timer.Elapsed += (s,e) => { - try{ - JSONObject controllerState = new JSONObject() - .Add("DriveControllers", new JSONArray().Add(Controller.DriveControllers.Select((dc=> new JSONObject() - .Add("slave", dc.Slave) - .Add("DriveController", dc.GetType().Name) - .Add("DriveState", dc.DriveState.ToString()) - )))) - .Add("DrivesState", Controller.DrivesState.ToString()) - .Add("IsRunning", Controller.IsRunning) - ; - websocket.Send(controllerState); - } catch (Exception ex) - { - Logging.Log(ex); - } - }; + // [GET("/sockets/controller/drives/:drive")] + // public HttpResponse GetDriveControllerSocket(int drive) + // { + // DriveController driveController = Controller.DriveControllers[drive]; - timer.Start(); - return websocket; - } + // Timer timer = new Timer(250); + // JSONWebSocketResponse websocket = new JSONWebSocketResponse(); + // websocket.OnWebSocketStateChanged += (Socket, newstate) => { + // if (newstate == WebSocketState.CLOSED) + // { + // timer.Stop(); + // timer.Dispose(); + // } + // }; - [GET("/sockets/controller/drives/:drive")] - public HttpResponse GetDriveControllerSocket(int drive) - { - DriveController driveController = Controller.DriveControllers[drive]; + // websocket.OnWebSocketReceivedText += (s,text) => { + // JSONObject message = (JSONObject)JSONParser.Parse(text); + // switch (message["event"].ToNative().ToString()) + // { + // case "action": + // Logging.Log(LogLevel.DEBUG, "DriveControllerSocket: action: {0}", message["value"].ToNative().ToString()); + // driveController.GetType().GetMethod(message["value"].ToNative().ToString()).Invoke(driveController, new object[0]{}); + // break; + // case "set": + // JSONObject jsonSet = (message["value"] as JSONObject); + // foreach (string key in jsonSet.Keys) + // { + // PropertyInfo propertyInfo = driveController.GetType().GetProperty(key); + // propertyInfo.SetValue(driveController, Cast.To(jsonSet[key].ToNative(), propertyInfo.PropertyType)); + // } + // break; + // } + // }; - Timer timer = new Timer(250); - JSONWebSocketResponse websocket = new JSONWebSocketResponse(); - websocket.OnWebSocketStateChanged += (Socket, newstate) => { - if (newstate == WebSocketState.CLOSED) - { - timer.Stop(); - timer.Dispose(); - } - }; + // timer.Elapsed += (s,e) => { + // try{ + // JSONObject driveControllerState = new JSONObject() + // .Add("id", drive) + // .Add("DriveState", driveController.DriveState.ToString()) + // .Add("OEMDriveState", driveController.OEMDriveState) + // .Add("DriveMode", driveController.DriveMode) + // .Add("OEMDriveMode", driveController.OEMDriveMode) + // ; + // websocket.Send(driveControllerState); + // } catch (Exception ex) + // { + // Logging.Log(ex); + // } + // }; - websocket.OnWebSocketReceivedText += (s,text) => { - JSONObject message = (JSONObject)JSONParser.Parse(text); - switch (message["event"].ToNative().ToString()) - { - case "action": - Logging.Log(LogLevel.DEBUG, "DriveControllerSocket: action: {0}", message["value"].ToNative().ToString()); - driveController.GetType().GetMethod(message["value"].ToNative().ToString()).Invoke(driveController, new object[0]{}); - break; - case "set": - JSONObject jsonSet = (message["value"] as JSONObject); - foreach (string key in jsonSet.Keys) - { - PropertyInfo propertyInfo = driveController.GetType().GetProperty(key); - propertyInfo.SetValue(driveController, Cast.To(jsonSet[key].ToNative(), propertyInfo.PropertyType)); - } - break; - } - }; - - timer.Elapsed += (s,e) => { - try{ - JSONObject driveControllerState = new JSONObject() - .Add("id", drive) - .Add("DriveState", driveController.DriveState.ToString()) - .Add("OEMDriveState", driveController.OEMDriveState) - .Add("DriveMode", driveController.DriveMode) - .Add("OEMDriveMode", driveController.OEMDriveMode) - ; - websocket.Send(driveControllerState); - } catch (Exception ex) - { - Logging.Log(ex); - } - }; - - timer.Start(); - return websocket; - } + // timer.Start(); + // return websocket; + // } } diff --git a/ln.ethercat.service/api/v1/ControllerWebSocket.cs b/ln.ethercat.service/api/v1/ControllerWebSocket.cs new file mode 100644 index 0000000..1707f79 --- /dev/null +++ b/ln.ethercat.service/api/v1/ControllerWebSocket.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using ln.ethercat.controller; +using ln.ethercat.controller.drives; +using ln.http.websocket; +using ln.json; +using ln.logging; + +namespace ln.ethercat.service.api.v1 +{ + public class ControllerWebSocket : WebSocketResponse + { + static List webSockets = new List(); + static bool sendingUpdates; + public static void SendUpdates(Controller controller) + { + lock (webSockets) + { + if (sendingUpdates) + return; + sendingUpdates = true; + } + + try + { + + JSONArray jsonDriveControllers = new JSONArray(); + + foreach (DriveController driveController in controller.ECMaster.DriveControllers) + { + JSONObject jsonDriveController = + new JSONObject() + .Add("DriveState", driveController.DriveState.ToString()) + .Add("OEMState", driveController.OEMDriveState) + .Add("DriveMode", driveController.DriveMode.ToString()) + .Add("TargetValue", driveController.TargetValue) + .Add("ActualSpeed", driveController.ActualSpeed) + .Add("ActualTorque", driveController.ActualTorque) + .Add("ActualLoad", driveController.ActualLoad) + ; + + jsonDriveControllers.Add(jsonDriveController); + } + + JSONObject jsonController = + new JSONObject() + .Add("State", controller.ControllerState.ToString()) + .Add("Drives", jsonDriveControllers) + .Add("CycleCounter", controller.CycleCounter) + ; + + + JSONObject jsonMessage = + new JSONObject() + .Add("event", "update") + .Add("value", jsonController) + ; + + string jsonMessageText = jsonMessage.ToString(); + + ControllerWebSocket[] currentSockets; + lock (webSockets) + currentSockets = webSockets.ToArray(); + + foreach (ControllerWebSocket webSocket in currentSockets) + { + if (webSocket.Controller.Equals(controller) && (webSocket.State == WebSocketState.OPEN)) + webSocket.Send(jsonMessageText); + } + } catch (Exception e) + { + Logging.Log(e); + } finally + { + sendingUpdates = false; + } + } + + Controller Controller; + + public ControllerWebSocket(Controller controller) + { + Controller = controller; + OnWebSocketStateChanged += (WebSocketResponse webSocket, WebSocketState newState) => { + switch (newState) + { + case WebSocketState.OPEN: + lock (webSockets) + webSockets.Add(this); + break; + default: + lock (webSockets) + webSockets.Remove(this); + break; + } + }; + } + + + public override void Received(string textMessage) + { + base.Received(textMessage); + + JSONValue jsonTextValue = JSONParser.Parse(textMessage); + if (jsonTextValue is JSONObject jsonMessage) + { + string eventName = jsonMessage["event"].ToNative().ToString(); + } + } + + + + } +} \ No newline at end of file diff --git a/ln.ethercat.service/api/v1/EthercatApiController.cs b/ln.ethercat.service/api/v1/EthercatApiController.cs index e5a8b0f..37b4226 100644 --- a/ln.ethercat.service/api/v1/EthercatApiController.cs +++ b/ln.ethercat.service/api/v1/EthercatApiController.cs @@ -1,18 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Timers; using ln.http; using ln.http.api; using ln.http.api.attributes; -using ln.http.websocket; using ln.json; -using ln.json.mapping; -using ln.logging; namespace ln.ethercat.service.api.v1 { - public class EthercatApiController : WebApiController { ECMaster ECMaster => EthercatService.ECMaster; @@ -51,14 +44,6 @@ namespace ln.ethercat.service.api.v1 [GET("/slaves/:slave/state")] public HttpResponse GetEthercatState(int slave) => HttpResponse.OK().Content(ECMaster.ReadSlaveState(slave).ToString()); - [POST("/slaves/:slave/state")] - public HttpResponse RequestEthercatState(int slave, ECSlaveState state) - { - if (ECMaster.RequestSlaveState(slave, state) > 0) - return HttpResponse.NoContent(); - return HttpResponse.GatewayTimeout(); - } - [GET("/master/state")] public HttpResponse GetFullState() diff --git a/ln.ethercat.service/api/v1/EthercatWebSocket.cs b/ln.ethercat.service/api/v1/EthercatWebSocket.cs index 8f20d31..23218af 100644 --- a/ln.ethercat.service/api/v1/EthercatWebSocket.cs +++ b/ln.ethercat.service/api/v1/EthercatWebSocket.cs @@ -10,6 +10,7 @@ using ln.http.websocket; using ln.json; using ln.json.mapping; using ln.logging; +using ln.type; namespace ln.ethercat.service.api.v1 { @@ -17,9 +18,19 @@ namespace ln.ethercat.service.api.v1 { /************** Static ***************************************************************************/ static List activeWebSockets = new List(); + static bool broadcastActive; public static void SendProcessData(ECMaster ecMaster) { + lock (activeWebSockets) + { + if (broadcastActive) + return; + broadcastActive = true; + } + + try{ + JSONArray jsonPDOList = new JSONArray(); foreach (PDO pdo in ecMaster.GetPDOMap()) @@ -49,11 +60,29 @@ namespace ln.ethercat.service.api.v1 ); string stateMessageText = stateMessage.ToString(); - foreach (EthercatWebSocket webSocket in activeWebSockets) + EthercatWebSocket[] sockets; + lock(activeWebSockets) + sockets = activeWebSockets.ToArray(); + + foreach (EthercatWebSocket webSocket in sockets) { - webSocket.Send(messageText); - webSocket.Send(stateMessageText); - webSocket.SendSubscribedSDOs(); + if (webSocket.State != WebSocketState.OPEN) + { + lock (activeWebSockets) + activeWebSockets.Remove(webSocket); + } else + { + webSocket.Send(messageText); + webSocket.Send(stateMessageText); + webSocket.SendSubscribedSDOs(); + } + } + } catch (Exception e) + { + Logging.Log(e); + } finally + { + broadcastActive = false; } } @@ -93,11 +122,13 @@ namespace ln.ethercat.service.api.v1 { case WebSocketState.OPEN: Logging.Log(LogLevel.DEBUG, "EthercatWebSocket -> active"); - activeWebSockets.Add(this); + lock(activeWebSockets) + activeWebSockets.Add(this); break; - case WebSocketState.CLOSED: + default: Logging.Log(LogLevel.DEBUG, "EthercatWebSocket -> inactive"); - activeWebSockets.Remove(this); + lock(activeWebSockets) + activeWebSockets.Remove(this); break; } } @@ -127,6 +158,28 @@ namespace ln.ethercat.service.api.v1 break; case "action": + break; + case "sdowrite": + if (jsonMessage["value"] is JSONObject jsonValue) + { + UInt16 slave_id, index; + byte subindex; + object value; + + slave_id = Cast.To(jsonValue["Slave"].ToNative()); + index = Cast.To(jsonValue["Index"].ToNative()); + subindex = Cast.To(jsonValue["SubIndex"].ToNative()); + value = jsonValue["Value"].ToNative(); + + if (EthercatService.ECMaster.GetSDOValue(slave_id, index, subindex, out SDOValue sdoValue)) + { + sdoValue.SetValue(value); + } else + { + Logging.Log(LogLevel.DEBUG, "EthercatWebSocket: event sdowrite: could not find SDOValue {0}:{1:X4}.{2}", slave_id, index, subindex); + } + } + break; } } diff --git a/ln.ethercat.service/www/html/spa.html b/ln.ethercat.service/www/html/spa.html index 5676f8c..db0a1d8 100644 --- a/ln.ethercat.service/www/html/spa.html +++ b/ln.ethercat.service/www/html/spa.html @@ -50,7 +50,11 @@