{"id":19991410,"url":"https://github.com/Rickedb/OpenProtocolInterpreter","last_synced_at":"2025-05-04T10:31:43.424Z","repository":{"id":21712376,"uuid":"86765491","full_name":"Rickedb/OpenProtocolInterpreter","owner":"Rickedb","description":"Converts the ugly ASCII package that came from Open Protocol to an object","archived":false,"fork":false,"pushed_at":"2025-02-10T01:15:03.000Z","size":18329,"stargazers_count":158,"open_issues_count":6,"forks_count":74,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-04-26T09:28:39.974Z","etag":null,"topics":["atlas","controller","copco","open-protocol","protocol","tightening"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Rickedb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-03-31T01:33:48.000Z","updated_at":"2025-04-05T00:20:43.000Z","dependencies_parsed_at":"2025-04-17T14:41:37.517Z","dependency_job_id":"ddc23d2f-ffc7-4699-825b-0337f4ea4c07","html_url":"https://github.com/Rickedb/OpenProtocolInterpreter","commit_stats":{"total_commits":537,"total_committers":8,"mean_commits":67.125,"dds":"0.027932960893854775","last_synced_commit":"2226813d4a843590fd07a5a86af6ba3642fcc396"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rickedb%2FOpenProtocolInterpreter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rickedb%2FOpenProtocolInterpreter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rickedb%2FOpenProtocolInterpreter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rickedb%2FOpenProtocolInterpreter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Rickedb","download_url":"https://codeload.github.com/Rickedb/OpenProtocolInterpreter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251767230,"owners_count":21640475,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["atlas","controller","copco","open-protocol","protocol","tightening"],"created_at":"2024-11-13T04:51:44.608Z","updated_at":"2025-05-04T10:31:38.413Z","avatar_url":"https://github.com/Rickedb.png","language":"C#","funding_links":["https://www.buymeacoffee.com/openprotocolitp","https://img.buymeacoffee.com/button-api/?text=Buy","https://opencollective.com/open-protocol-interpreter"],"categories":["C\\#"],"sub_categories":[],"readme":"\u003cimg src=\"https://github.com/Rickedb/OpenProtocolInterpreter/blob/master/media/logo.png?raw=true\" width=\"550\" alt=\"Open Protocol Interpreter\" /\u003e\n\n\u003ch1\u003e\n   \u003ca href=\"https://www.buymeacoffee.com/openprotocolitp\"\u003e\n    \u003cimg height=\"40\" alt=\"Buy me a coffee\" src=\"https://img.buymeacoffee.com/button-api/?text=Buy me a coffee\u0026emoji=\u0026slug=openprotocolitp\u0026button_colour=FFDD00\u0026font_colour=000000\u0026font_family=Cookie\u0026outline_colour=000000\u0026coffee_colour=ffffff\" /\u003e\n   \u003c/a\u003e\n  \u003ca href=\"https://opencollective.com/open-protocol-interpreter\" alt=\"Financial Contributors on Open Collective\"\u003e\n    \u003cimg src=\"https://opencollective.com/open-protocol-interpreter/all/badge.svg?label=financial+contributors\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/Rickedb/OpenProtocolInterpreter/\"\u003e\n    \u003cimg src=\"https://github.com/rickedb/openprotocolinterpreter/workflows/master/badge.svg\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://raw.githubusercontent.com/Rickedb/OpenProtocolIntepreter/master/LICENSE\"\u003e\n    \u003cimg src= \"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"MIT\"\u003e\n  \u003c/a\u003e\n\u003c/h1\u003e\n\n  \n\u003e OpenProtocol communication utility\n\n 1. [What is Open Protocol at all?](#what-is-open-protocol-at-all) \n 2. [What is OpenProtocolInterpreter?](#what-is-openprotocolinterpreter)\n 3. [How does it work?](#how-does-it-work)\n 4. [Usage examples](#lets-see-some-examples-of-usage)\n 5. [Available on package managers](#get-it-on-nuget)\n 6. [Advanced section](#advanced-section)\n    * [How it was built?](#how-it-was-built)\n    * [Customization](#mids-identifying-customization)\n    * [Advanced example](#advanced-example)\n7. [Tips](#tips)\n8. [Contribute to the project](#contribute-to-the-project)\n9. [Still unavailable mids](#list-of-still-unavailable-mids)\n\n\n## What is Open Protocol at all?\n\nOpen Protocol, as the name says, it's a protocol to communicate with Atlas Copco Tightening Controllers or whatever that implement that protocol.\nMost common Tightening Controllers from Atlas Copco company are **PowerFocus4000** and **PowerMacs**.\n\n*Although, some other companies adhered to use the same protocol.*\n\n## What is OpenProtocolInterpreter?\n\nOpenProtocolInterpreter is a **library that converts the ugly string** that came from Open Protocol packages, which is commonly called **MID**, to an **object**.\n*\"Substringing\"* packages is such a boring thing to do, so let OpenProtocolIntepreter do it for you!\n\n**[If you're curious, just take a look at their documentation.](https://github.com/Rickedb/OpenProtocolIntepreter/blob/master/docs/OpenProtocol_Specification.pdf)**\n\n## How does it work?\n\nIt's simple, you give us your byte[] or string package and we deliver you an object, simple as that!\n\nFor example, let's imagine you received the following string package: \n``` csharp\nstring package = \"00240005001         0018\";\n```\nIt's **MID 5**, so OpenProtocolIntepreter will return a **Mid0005** class for you with all his datafields and the package entire translated to an object.\n\n## Let's see some examples of usage\n\nA simple usage:\n\n``` csharp\nvar interpreter = new MidInterpreter();\nvar midPackage = @\"00260004001         001802\";\nvar myMid04 = interpreter.Parse\u003cMid0004\u003e(midPackage);\n//MID 0004 is an error mid which contains which MID Failed and its error code\n//Int value of the Failed Mid\nint myFailedMid = myMid04.FailedMid;\n//An enum with Error Code\nError errorCode = myMid04.ErrorCode;\n```   \n\nIt can generate an object from a string, but can it make it to the other way?? FOR SURE!\n``` csharp\nvar jobUploadRequest = new Mid0032(1, 2); //Job id 1, revision 2\nvar package = jobUploadRequest.Pack();\n//Generated package =\u003e 00240032002         0001\n```  \n\n## Get it on [NuGet](https://www.nuget.org/packages/OpenProtocolInterpreter)!\n```\nInstall-Package OpenProtocolInterpreter\n```\n\n## Advanced Section!\n\nNow we will get real!\nPut one thing in mind, in real world we will always need to build something more complex than the dummies examples we give to you.\n**With this in mind, this section is for you:**\n\n#### How it was built?\n\nIt used to rely on **Chain Of Responsabilities** design pattern, but since we had some problems with instance references, it changed!\nFor now, instead of iterating through all Mids of the same category, it relies on a Dictionary, which every category knows which mid it attends, once it found it creates a new Instance via System.Reflection and parse it.\n\n#### MIDs Identifying Customization\n\nWe have several MIDs inside Open Protocol documentation, but do you really need all of them? \nThe answer is... **NO!**\n\nYou will probably need only to use a range of MIDs, with this in mind, we did something to make things faster. You can tell us which MIDs we should considerate!\n\n\u003e *NOTE: You can register only mids you need to call \"Parse\" method \n\nHere is an example:\n``` csharp\nstring package = \"00260004001         001802\";\nvar myCustomInterpreter = new MidInterpreter()\n\t\t\t\t\t\t\t\t.UseAllMessages(new Type[]\n\t\t                        {\n\t\t                            typeof(Mid0001),\n\t\t                            typeof(Mid0002),\n\t\t                            typeof(Mid0003),\n\t\t                            typeof(Mid0004),\n\t\t                            typeof(Mid0106)\n\t\t                        });\n//Will work:\nvar myMid04 = myCustomInterpreter.Parse\u003cMid0004\u003e(package);\n//Won't work, will throw NotImplementedException:\nvar myMid30 = myCustomInterpreter.Parse\u003cMid0030\u003e(package);        \n//Won't work, will throw InvalidCastException:\nvar myMid01 = myCustomInterpreter.Parse\u003cMid0001\u003e(package);\n```\nWhen you don't know which package will come, use ``` Parse ``` overload, not ``` Parse\u003cDesiredMid\u003e ```. If you want, take a look at the sample on this repository.\n\u003e If necessary, there is a new overload where you can define if you're the controller or the integrator, which will automatically handle implemented mids\n\n#### MIDs Overriding\n\nMaybe you have a totally crazy controller that does not implement the Mid as the documentation says or you might want to inject your own Mid inheriting another Mid, \nso you can customize it and add more properties to handle some conversions. Anyway, if you need that, it's possible to override!\n\nHere is an example:\n``` csharp\n//This will override Mid 81 with my custom Mid\nvar _midInterpreter new MidInterpreter().UseAllMessages()\n                                        .UseTimeMessages(new Dictionary\u003cint, Type\u003e() { { 81, typeof(OverridedMid0081) } });\n\n\npublic class OverridedMid0081 : Mid0081\n{\n    public string FormattedDate\n    {\n        get =\u003e Time.ToString(\"dd/MM/yyyy HH:mm:ss\");\n        set =\u003e Time = DateTime.Parse(value);\n    }\n\n    public OverridedMid0081()\n    {\n        \n    }\n\n    public override string Pack()\n    {\n        Time = TestCustomMid.Now;\n        return base.Pack();\n    }\n}\n```\n\n#### Adding MIDs that are not in documentation\n\nMaybe your controller is weird and have unknown MID numbers, MIDs that are not in the documentation and you want to inject into MidInterpreter, there is a way:\n\n``` csharp\nvar _midInterpreter new MidInterpreter().UseAllMessages()\n                                        .UseCustomMessage(new Dictionary\u003cint, Type\u003e() { { 83, typeof(NewMid0083) } });\n\npublic class NewMid0083 : Mid\n{\n    private readonly IValueConverter\u003cDateTime\u003e _dateConverter;\n    private const int LAST_REVISION = 1;\n    public const int MID = 83;\n\n    public DateTime Time\n    {\n        get =\u003e GetField(1, (int)DataFields.TIME).GetValue(_dateConverter.Convert);\n        set =\u003e GetField(1, (int)DataFields.TIME).SetValue(_dateConverter.Convert, value);\n    }\n    public string TimeZone\n    {\n        get =\u003e GetField(1, (int)DataFields.TIMEZONE).Value;\n        set =\u003e GetField(1, (int)DataFields.TIMEZONE).SetValue(value);\n    }\n\n    public NewMid0083() : base(MID, LAST_REVISION)\n    {\n        _dateConverter = new DateConverter();\n    }\n\n    protected override Dictionary\u003cint, List\u003cDataField\u003e\u003e RegisterDatafields()\n    {\n        return new Dictionary\u003cint, List\u003cDataField\u003e\u003e()\n        {\n            {\n                1, new List\u003cDataField\u003e()\n                        {\n                            new DataField((int)DataFields.TIME, 20, 19),\n                            new DataField((int)DataFields.TIMEZONE, 41, 2)\n                        }\n            }\n        };\n    }\n\n    public enum DataFields\n    {\n        TIME,\n        TIMEZONE\n    }\n}\n```\n\n\u003e **NOTE:** Custom messages might not perform as fast as other MIDs because they don't have optimizations for finding it\n\n#### Advanced Example\n\nDeclared a delegate:\n\n``` csharp\nprotected delegate void ReceivedCommandActionDelegate(ReceivedMIDEventArgs e);\n```\n**ReceivedMIDEventArgs class**:\n``` csharp\npublic class ReceivedMidEventArgs : EventArgs\n{\n    public Mid ReceivedMid { get; set; }\n}\n```\nCreated a method to register all those MID types by delegates:\n\n``` csharp\nprotected Dictionary\u003cType, ReceivedCommandActionDelegate\u003e RegisterOnAsyncReceivedMids()\n{\n    var receivedMids = new Dictionary\u003cType, ReceivedCommandActionDelegate\u003e();\n    receivedMids.Add(typeof(Mid0005), new ReceivedCommandActionDelegate(OnCommandAcceptedReceived));\n    receivedMids.Add(typeof(Mid0004), new ReceivedCommandActionDelegate(OnErrorReceived));\n    receivedMids.Add(typeof(Mid0071), new ReceivedCommandActionDelegate(OnAlarmReceived));\n    receivedMids.Add(typeof(Mid0061), new ReceivedCommandActionDelegate(OnTighteningReceived));\n    receivedMids.Add(typeof(Mid0035), new ReceivedCommandActionDelegate(OnJobInfoReceived));\n    return receivedMids;\n}\n```\nWhat was done is registering in a dictionary the correspondent delegate for a determinated MID, once done that we just need to invoke the delegate everytime you face a desired MID.\n\nWhen a package income:\n\n``` csharp\nprotected void OnPackageReceived(string message)\n{\n    try\n    {\n        //Parse to mid class\n        var mid = Interpreter.Parse(message);\n\n        //Get Registered delegate for the MID that was identified\n        var action = OnReceivedMid.FirstOrDefault(x =\u003e x.Key == mid.GetType());\n        \n        if (action.Equals(default(KeyValuePair\u003cType, ReceivedCommandActionDelegate\u003e)))\n           return; //Stop if there is no delegate registered for the message that arrived\n\n         action.Value(new ReceivedMidEventArgs() { ReceivedMid = mid }); //Call delegate\n     }\n     catch (Exception ex)\n     {\n        Console.log(ex.Message);\n     }\n}\n```\nThis would call the registered delegate which you're sure what mid it is. \nFor example when a **MID_0061** (last tightening) pop up, the  **onTighteningReceived** delegate will be called:\n\n``` csharp\nprotected void OnTighteningReceived(ReceivedMidEventArgs e)\n{\n    try\n    {\n        Mid0061 tighteningMid = e.ReceivedMID as Mid0061; //Casting to the right mid\n\n        //This method just send the ack from tightening mid\n        BuildAndSendAcknowledge(tighteningMid); \n        Console.log(\"TIGHTENING ARRIVED\")\n     }\n     catch (Exception ex)\n     {\n         Console.log(ex.Message);\n     }\n}\n\nprotected void BuildAndSendAcknowledge(Mid mid)\n{\n     TcpClient.GetStream().Write(new Mid0062().Pack()); //Send acknowledge to controller\n}\n```\n\n### Tips\n\n\u003e Instantiate the **MIDIdentifier** class just once and keep working with it!\n\n\u003e **Controller Implementation Tip:** Always **TRY** to register used MIDs, not all Tightening Controllers use every available MID.\n\n\u003e **Integrator Implementation Tip:** Always **DO** register used MIDs, I'm pretty sure you won't need all of them to your application.\n\n### Contribute to the project\n\nLot's of effort were given to this project and by seen people using it motivated me a lot to improve it more and more.\n\nDoes it help you a lot? That's awesome and very rewarding!\nBut if you wish, you can support and help to motivate the constant improving of this library by contributing in [OpenCollective](https://opencollective.com/open-protocol-interpreter).\n\n### List of still unavailable Mids\n\n - Mid 0700;\n - Mid 0900;\n - Mid 0901;\n - Mid 1000;\n - Mid 1001;\n - Mid 1601;\n - Mid 1602;\n - Mid 1900;\n - Mid 1901;\n - Mid 2100;\n - Mid 2500;\n - Mid 2501;\n - Mid 2600;\n - Mid 2601;\n - Mid 2602;\n - Mid 2603;\n - Mid 2604;\n - Mid 2605;\n - Mid 2606.\n\nFeel free to fork and contribute to add any of those mids.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRickedb%2FOpenProtocolInterpreter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRickedb%2FOpenProtocolInterpreter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRickedb%2FOpenProtocolInterpreter/lists"}