├── .gitattributes ├── .gitignore ├── LICENSE ├── MinerControl.sln ├── MinerControl ├── AlgorithmEntry.cs ├── App.config ├── Extensions.cs ├── Hardware │ ├── NVAPI.cs │ └── PInvokeDelegateFactory.cs ├── MainWindow.Designer.cs ├── MainWindow.cs ├── MainWindow.resx ├── MinerControl.conf ├── MinerControl.csproj ├── MiningEngine.cs ├── MiningModeEnum.cs ├── PriceEntries │ ├── HamsterPoolPriceEntry.cs │ ├── LtcRabbitPriceEntry.cs │ ├── ManualPriceEntry.cs │ ├── NiceHashPriceEntry.cs │ ├── PriceEntryBase.cs │ ├── TradeMyBitPriceEntry.cs │ ├── WafflePoolPriceEntry.cs │ ├── WePayBtcPriceEntry.cs │ └── YaampPriceEntry.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── ServiceEnum.cs ├── Services │ ├── HamsterPoolService.cs │ ├── IService.cs │ ├── LtcRabbitService.cs │ ├── ManualService.cs │ ├── NiceHashService.cs │ ├── NiceHashServiceBase.cs │ ├── ServiceBase.cs │ ├── TradeMyBitService.cs │ ├── WafflePoolService.cs │ ├── WePayBtcService.cs │ ├── WestHashService.cs │ └── YaampService.cs ├── Utility │ ├── ErrorLogger.cs │ ├── Multicast │ │ ├── MulticastDataReceivedEventArgs.cs │ │ ├── MulticastReceiver.cs │ │ └── MulticastSender.cs │ ├── ProcessUtil.cs │ ├── PropertyChangedBase.cs │ ├── SlidingBuffer.cs │ ├── SortableBindingList.cs │ └── WebUtil.cs ├── bitcoin.ico └── test-run.bat ├── README.md └── TestMiner ├── App.config ├── Program.cs ├── Properties └── AssemblyInfo.cs └── TestMiner.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 100 | #packages/ 101 | 102 | # Windows Azure Build Output 103 | csx 104 | *.build.csdef 105 | 106 | # Windows Store app package directory 107 | AppPackages/ 108 | 109 | # Others 110 | sql/ 111 | *.Cache 112 | ClientBin/ 113 | [Ss]tyle[Cc]op.* 114 | ~$* 115 | *~ 116 | *.dbmdl 117 | *.[Pp]ublish.xml 118 | *.pfx 119 | *.publishsettings 120 | 121 | # RIA/Silverlight projects 122 | Generated_Code/ 123 | 124 | # Backup & report files from converting an old project file to a newer 125 | # Visual Studio version. Backup files are not needed, because we have git ;-) 126 | _UpgradeReport_Files/ 127 | Backup*/ 128 | UpgradeLog*.XML 129 | UpgradeLog*.htm 130 | 131 | # SQL Server files 132 | App_Data/*.mdf 133 | App_Data/*.ldf 134 | 135 | 136 | #LightSwitch generated files 137 | GeneratedArtifacts/ 138 | _Pvt_Extensions/ 139 | ModelManifest.xml 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac desktop service store files 156 | .DS_Store 157 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /MinerControl.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MinerControl", "MinerControl\MinerControl.csproj", "{94CC3F87-4215-4F3F-9524-695BF16FEBD0}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestMiner", "TestMiner\TestMiner.csproj", "{63721D21-B76A-40AE-9A37-0C6F392CEE63}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {94CC3F87-4215-4F3F-9524-695BF16FEBD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {94CC3F87-4215-4F3F-9524-695BF16FEBD0}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {94CC3F87-4215-4F3F-9524-695BF16FEBD0}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {94CC3F87-4215-4F3F-9524-695BF16FEBD0}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {63721D21-B76A-40AE-9A37-0C6F392CEE63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {63721D21-B76A-40AE-9A37-0C6F392CEE63}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {63721D21-B76A-40AE-9A37-0C6F392CEE63}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {63721D21-B76A-40AE-9A37-0C6F392CEE63}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /MinerControl/AlgorithmEntry.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace MinerControl 3 | { 4 | public class AlgorithmEntry 5 | { 6 | public string Name { get; set; } 7 | public string Display { get; set; } 8 | public decimal Hashrate { get; set; } 9 | public decimal Power { get; set; } 10 | 11 | public string Param1 { get; set; } 12 | public string Param2 { get; set; } 13 | public string Param3 { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /MinerControl/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /MinerControl/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace MinerControl 8 | { 9 | public static class Extensions 10 | { 11 | public static string FormatTime(this TimeSpan timeSpan, bool zeroAsEmpty = false) 12 | { 13 | if (timeSpan == TimeSpan.Zero && zeroAsEmpty) 14 | return string.Empty; 15 | 16 | return timeSpan.TotalDays > 1 17 | ? timeSpan.ToString(@"dd\.hh\:mm\:ss") 18 | : timeSpan.ToString(@"hh\:mm\:ss"); 19 | } 20 | 21 | public static string FormatTime(this TimeSpan? timeSpan) 22 | { 23 | if (!timeSpan.HasValue) 24 | return string.Empty; 25 | 26 | return timeSpan.Value.FormatTime(); 27 | } 28 | 29 | public static void AddRange(this IList list, IEnumerable items) 30 | { 31 | foreach (var item in items) 32 | list.Add(item); 33 | } 34 | 35 | public static decimal ExtractDecimal(this object raw) 36 | { 37 | var decimalValue = raw as decimal?; 38 | if (decimalValue.HasValue) return decimalValue.Value; 39 | 40 | var doubleValue = raw as double?; 41 | if (doubleValue.HasValue) return (decimal)doubleValue.Value; 42 | 43 | var floatValue = raw as float?; 44 | if (floatValue.HasValue) return (decimal)floatValue.Value; 45 | 46 | var longValue = raw as long?; 47 | if (longValue.HasValue) return (decimal)longValue.Value; 48 | 49 | var intValue = raw as int?; 50 | if (intValue.HasValue) return (decimal)intValue.Value; 51 | 52 | decimal parseValue; 53 | var style = NumberStyles.AllowDecimalPoint; 54 | var culture = CultureInfo.CreateSpecificCulture("en-US"); 55 | 56 | if (decimal.TryParse(raw.ToString(), style, culture, out parseValue)) return parseValue; 57 | 58 | return 0; 59 | } 60 | 61 | public static string GetString(this IDictionary data, string key) 62 | { 63 | if (!data.ContainsKey(key)) return null; 64 | return data[key] as string; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /MinerControl/Hardware/NVAPI.cs: -------------------------------------------------------------------------------- 1 | //http://open-hardware-monitor.googlecode.com/svn/trunk/Hardware/ 2 | 3 | /* 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | Copyright (C) 2009-2012 Michael Möller 10 | Copyright (C) 2011 Christian Vallières 11 | 12 | */ 13 | 14 | using System; 15 | using System.Runtime.InteropServices; 16 | using System.Text; 17 | 18 | namespace OpenHardwareMonitor.Hardware.Nvidia 19 | { 20 | 21 | internal enum NvStatus 22 | { 23 | OK = 0, 24 | ERROR = -1, 25 | LIBRARY_NOT_FOUND = -2, 26 | NO_IMPLEMENTATION = -3, 27 | API_NOT_INTIALIZED = -4, 28 | INVALID_ARGUMENT = -5, 29 | NVIDIA_DEVICE_NOT_FOUND = -6, 30 | END_ENUMERATION = -7, 31 | INVALID_HANDLE = -8, 32 | INCOMPATIBLE_STRUCT_VERSION = -9, 33 | HANDLE_INVALIDATED = -10, 34 | OPENGL_CONTEXT_NOT_CURRENT = -11, 35 | NO_GL_EXPERT = -12, 36 | INSTRUMENTATION_DISABLED = -13, 37 | EXPECTED_LOGICAL_GPU_HANDLE = -100, 38 | EXPECTED_PHYSICAL_GPU_HANDLE = -101, 39 | EXPECTED_DISPLAY_HANDLE = -102, 40 | INVALID_COMBINATION = -103, 41 | NOT_SUPPORTED = -104, 42 | PORTID_NOT_FOUND = -105, 43 | EXPECTED_UNATTACHED_DISPLAY_HANDLE = -106, 44 | INVALID_PERF_LEVEL = -107, 45 | DEVICE_BUSY = -108, 46 | NV_PERSIST_FILE_NOT_FOUND = -109, 47 | PERSIST_DATA_NOT_FOUND = -110, 48 | EXPECTED_TV_DISPLAY = -111, 49 | EXPECTED_TV_DISPLAY_ON_DCONNECTOR = -112, 50 | NO_ACTIVE_SLI_TOPOLOGY = -113, 51 | SLI_RENDERING_MODE_NOTALLOWED = -114, 52 | EXPECTED_DIGITAL_FLAT_PANEL = -115, 53 | ARGUMENT_EXCEED_MAX_SIZE = -116, 54 | DEVICE_SWITCHING_NOT_ALLOWED = -117, 55 | TESTING_CLOCKS_NOT_SUPPORTED = -118, 56 | UNKNOWN_UNDERSCAN_CONFIG = -119, 57 | TIMEOUT_RECONFIGURING_GPU_TOPO = -120, 58 | DATA_NOT_FOUND = -121, 59 | EXPECTED_ANALOG_DISPLAY = -122, 60 | NO_VIDLINK = -123, 61 | REQUIRES_REBOOT = -124, 62 | INVALID_HYBRID_MODE = -125, 63 | MIXED_TARGET_TYPES = -126, 64 | SYSWOW64_NOT_SUPPORTED = -127, 65 | IMPLICIT_SET_GPU_TOPOLOGY_CHANGE_NOT_ALLOWED = -128, 66 | REQUEST_USER_TO_CLOSE_NON_MIGRATABLE_APPS = -129, 67 | OUT_OF_MEMORY = -130, 68 | WAS_STILL_DRAWING = -131, 69 | FILE_NOT_FOUND = -132, 70 | TOO_MANY_UNIQUE_STATE_OBJECTS = -133, 71 | INVALID_CALL = -134, 72 | D3D10_1_LIBRARY_NOT_FOUND = -135, 73 | FUNCTION_NOT_FOUND = -136 74 | } 75 | 76 | internal enum NvThermalController 77 | { 78 | NONE = 0, 79 | GPU_INTERNAL, 80 | ADM1032, 81 | MAX6649, 82 | MAX1617, 83 | LM99, 84 | LM89, 85 | LM64, 86 | ADT7473, 87 | SBMAX6649, 88 | VBIOSEVT, 89 | OS, 90 | UNKNOWN = -1, 91 | } 92 | 93 | internal enum NvThermalTarget 94 | { 95 | NONE = 0, 96 | GPU = 1, 97 | MEMORY = 2, 98 | POWER_SUPPLY = 4, 99 | BOARD = 8, 100 | ALL = 15, 101 | UNKNOWN = -1 102 | }; 103 | 104 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 105 | internal struct NvSensor 106 | { 107 | public NvThermalController Controller; 108 | public uint DefaultMinTemp; 109 | public uint DefaultMaxTemp; 110 | public uint CurrentTemp; 111 | public NvThermalTarget Target; 112 | } 113 | 114 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 115 | internal struct NvGPUThermalSettings 116 | { 117 | public uint Version; 118 | public uint Count; 119 | [MarshalAs(UnmanagedType.ByValArray, 120 | SizeConst = NVAPI.MAX_THERMAL_SENSORS_PER_GPU)] 121 | public NvSensor[] Sensor; 122 | } 123 | 124 | [StructLayout(LayoutKind.Sequential)] 125 | internal struct NvDisplayHandle 126 | { 127 | private readonly IntPtr ptr; 128 | } 129 | 130 | [StructLayout(LayoutKind.Sequential)] 131 | internal struct NvPhysicalGpuHandle 132 | { 133 | private readonly IntPtr ptr; 134 | } 135 | 136 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 137 | internal struct NvClocks 138 | { 139 | public uint Version; 140 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = NVAPI.MAX_CLOCKS_PER_GPU)] 141 | public uint[] Clock; 142 | } 143 | 144 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 145 | internal struct NvPState 146 | { 147 | public bool Present; 148 | public int Percentage; 149 | } 150 | 151 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 152 | internal struct NvPStates 153 | { 154 | public uint Version; 155 | public uint Flags; 156 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = NVAPI.MAX_PSTATES_PER_GPU)] 157 | public NvPState[] PStates; 158 | } 159 | 160 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 161 | internal struct NvUsages 162 | { 163 | public uint Version; 164 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = NVAPI.MAX_USAGES_PER_GPU)] 165 | public uint[] Usage; 166 | } 167 | 168 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 169 | internal struct NvCooler 170 | { 171 | public int Type; 172 | public int Controller; 173 | public int DefaultMin; 174 | public int DefaultMax; 175 | public int CurrentMin; 176 | public int CurrentMax; 177 | public int CurrentLevel; 178 | public int DefaultPolicy; 179 | public int CurrentPolicy; 180 | public int Target; 181 | public int ControlType; 182 | public int Active; 183 | } 184 | 185 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 186 | internal struct NvGPUCoolerSettings 187 | { 188 | public uint Version; 189 | public uint Count; 190 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = NVAPI.MAX_COOLER_PER_GPU)] 191 | public NvCooler[] Cooler; 192 | } 193 | 194 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 195 | internal struct NvLevel 196 | { 197 | public int Level; 198 | public int Policy; 199 | } 200 | 201 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 202 | internal struct NvGPUCoolerLevels 203 | { 204 | public uint Version; 205 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = NVAPI.MAX_COOLER_PER_GPU)] 206 | public NvLevel[] Levels; 207 | } 208 | 209 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 210 | internal struct NvMemoryInfo 211 | { 212 | public uint Version; 213 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 214 | NVAPI.MAX_MEMORY_VALUES_PER_GPU)] 215 | public uint[] Values; 216 | } 217 | 218 | [StructLayout(LayoutKind.Sequential, Pack = 8)] 219 | internal struct NvDisplayDriverVersion 220 | { 221 | public uint Version; 222 | public uint DriverVersion; 223 | public uint BldChangeListNum; 224 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.SHORT_STRING_MAX)] 225 | public string BuildBranch; 226 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.SHORT_STRING_MAX)] 227 | public string Adapter; 228 | } 229 | 230 | internal class NVAPI 231 | { 232 | 233 | public const int MAX_PHYSICAL_GPUS = 64; 234 | public const int SHORT_STRING_MAX = 64; 235 | 236 | public const int MAX_THERMAL_SENSORS_PER_GPU = 3; 237 | public const int MAX_CLOCKS_PER_GPU = 0x120; 238 | public const int MAX_PSTATES_PER_GPU = 8; 239 | public const int MAX_USAGES_PER_GPU = 33; 240 | public const int MAX_COOLER_PER_GPU = 20; 241 | public const int MAX_MEMORY_VALUES_PER_GPU = 5; 242 | 243 | public static readonly uint GPU_THERMAL_SETTINGS_VER = (uint) 244 | Marshal.SizeOf(typeof(NvGPUThermalSettings)) | 0x10000; 245 | public static readonly uint GPU_CLOCKS_VER = (uint) 246 | Marshal.SizeOf(typeof(NvClocks)) | 0x20000; 247 | public static readonly uint GPU_PSTATES_VER = (uint) 248 | Marshal.SizeOf(typeof(NvPStates)) | 0x10000; 249 | public static readonly uint GPU_USAGES_VER = (uint) 250 | Marshal.SizeOf(typeof(NvUsages)) | 0x10000; 251 | public static readonly uint GPU_COOLER_SETTINGS_VER = (uint) 252 | Marshal.SizeOf(typeof(NvGPUCoolerSettings)) | 0x20000; 253 | public static readonly uint GPU_MEMORY_INFO_VER = (uint) 254 | Marshal.SizeOf(typeof(NvMemoryInfo)) | 0x20000; 255 | public static readonly uint DISPLAY_DRIVER_VERSION_VER = (uint) 256 | Marshal.SizeOf(typeof(NvDisplayDriverVersion)) | 0x10000; 257 | public static readonly uint GPU_COOLER_LEVELS_VER = (uint) 258 | Marshal.SizeOf(typeof(NvGPUCoolerLevels)) | 0x10000; 259 | 260 | private delegate IntPtr nvapi_QueryInterfaceDelegate(uint id); 261 | private delegate NvStatus NvAPI_InitializeDelegate(); 262 | private delegate NvStatus NvAPI_GPU_GetFullNameDelegate( 263 | NvPhysicalGpuHandle gpuHandle, StringBuilder name); 264 | 265 | public delegate NvStatus NvAPI_GPU_GetThermalSettingsDelegate( 266 | NvPhysicalGpuHandle gpuHandle, int sensorIndex, 267 | ref NvGPUThermalSettings nvGPUThermalSettings); 268 | public delegate NvStatus NvAPI_EnumNvidiaDisplayHandleDelegate(int thisEnum, 269 | ref NvDisplayHandle displayHandle); 270 | public delegate NvStatus NvAPI_GetPhysicalGPUsFromDisplayDelegate( 271 | NvDisplayHandle displayHandle, [Out] NvPhysicalGpuHandle[] gpuHandles, 272 | out uint gpuCount); 273 | public delegate NvStatus NvAPI_EnumPhysicalGPUsDelegate( 274 | [Out] NvPhysicalGpuHandle[] gpuHandles, out int gpuCount); 275 | public delegate NvStatus NvAPI_GPU_GetTachReadingDelegate( 276 | NvPhysicalGpuHandle gpuHandle, out int value); 277 | public delegate NvStatus NvAPI_GPU_GetAllClocksDelegate( 278 | NvPhysicalGpuHandle gpuHandle, ref NvClocks nvClocks); 279 | public delegate NvStatus NvAPI_GPU_GetPStatesDelegate( 280 | NvPhysicalGpuHandle gpuHandle, ref NvPStates nvPStates); 281 | public delegate NvStatus NvAPI_GPU_GetUsagesDelegate( 282 | NvPhysicalGpuHandle gpuHandle, ref NvUsages nvUsages); 283 | public delegate NvStatus NvAPI_GPU_GetCoolerSettingsDelegate( 284 | NvPhysicalGpuHandle gpuHandle, int coolerIndex, 285 | ref NvGPUCoolerSettings nvGPUCoolerSettings); 286 | public delegate NvStatus NvAPI_GPU_SetCoolerLevelsDelegate( 287 | NvPhysicalGpuHandle gpuHandle, int coolerIndex, 288 | ref NvGPUCoolerLevels NvGPUCoolerLevels); 289 | public delegate NvStatus NvAPI_GPU_GetMemoryInfoDelegate( 290 | NvDisplayHandle displayHandle, ref NvMemoryInfo nvMemoryInfo); 291 | public delegate NvStatus NvAPI_GetDisplayDriverVersionDelegate( 292 | NvDisplayHandle displayHandle, [In, Out] ref NvDisplayDriverVersion 293 | nvDisplayDriverVersion); 294 | public delegate NvStatus NvAPI_GetInterfaceVersionStringDelegate( 295 | StringBuilder version); 296 | public delegate NvStatus NvAPI_GPU_GetPCIIdentifiersDelegate( 297 | NvPhysicalGpuHandle gpuHandle, out uint deviceId, out uint subSystemId, 298 | out uint revisionId, out uint extDeviceId); 299 | 300 | private static readonly bool available; 301 | private static readonly nvapi_QueryInterfaceDelegate nvapi_QueryInterface; 302 | private static readonly NvAPI_InitializeDelegate NvAPI_Initialize; 303 | private static readonly NvAPI_GPU_GetFullNameDelegate 304 | _NvAPI_GPU_GetFullName; 305 | private static readonly NvAPI_GetInterfaceVersionStringDelegate 306 | _NvAPI_GetInterfaceVersionString; 307 | 308 | public static readonly NvAPI_GPU_GetThermalSettingsDelegate 309 | NvAPI_GPU_GetThermalSettings; 310 | public static readonly NvAPI_EnumNvidiaDisplayHandleDelegate 311 | NvAPI_EnumNvidiaDisplayHandle; 312 | public static readonly NvAPI_GetPhysicalGPUsFromDisplayDelegate 313 | NvAPI_GetPhysicalGPUsFromDisplay; 314 | public static readonly NvAPI_EnumPhysicalGPUsDelegate 315 | NvAPI_EnumPhysicalGPUs; 316 | public static readonly NvAPI_GPU_GetTachReadingDelegate 317 | NvAPI_GPU_GetTachReading; 318 | public static readonly NvAPI_GPU_GetAllClocksDelegate 319 | NvAPI_GPU_GetAllClocks; 320 | public static readonly NvAPI_GPU_GetPStatesDelegate 321 | NvAPI_GPU_GetPStates; 322 | public static readonly NvAPI_GPU_GetUsagesDelegate 323 | NvAPI_GPU_GetUsages; 324 | public static readonly NvAPI_GPU_GetCoolerSettingsDelegate 325 | NvAPI_GPU_GetCoolerSettings; 326 | public static readonly NvAPI_GPU_SetCoolerLevelsDelegate 327 | NvAPI_GPU_SetCoolerLevels; 328 | public static readonly NvAPI_GPU_GetMemoryInfoDelegate 329 | NvAPI_GPU_GetMemoryInfo; 330 | public static readonly NvAPI_GetDisplayDriverVersionDelegate 331 | NvAPI_GetDisplayDriverVersion; 332 | public static readonly NvAPI_GPU_GetPCIIdentifiersDelegate 333 | NvAPI_GPU_GetPCIIdentifiers; 334 | 335 | private NVAPI() { } 336 | 337 | public static NvStatus NvAPI_GPU_GetFullName(NvPhysicalGpuHandle gpuHandle, 338 | out string name) 339 | { 340 | StringBuilder builder = new StringBuilder(SHORT_STRING_MAX); 341 | NvStatus status; 342 | if (_NvAPI_GPU_GetFullName != null) 343 | status = _NvAPI_GPU_GetFullName(gpuHandle, builder); 344 | else 345 | status = NvStatus.FUNCTION_NOT_FOUND; 346 | name = builder.ToString(); 347 | return status; 348 | } 349 | 350 | public static NvStatus NvAPI_GetInterfaceVersionString(out string version) 351 | { 352 | StringBuilder builder = new StringBuilder(SHORT_STRING_MAX); 353 | NvStatus status; 354 | if (_NvAPI_GetInterfaceVersionString != null) 355 | status = _NvAPI_GetInterfaceVersionString(builder); 356 | else 357 | status = NvStatus.FUNCTION_NOT_FOUND; 358 | version = builder.ToString(); 359 | return status; 360 | } 361 | 362 | private static string GetDllName() 363 | { 364 | if (IntPtr.Size == 4) 365 | { 366 | return "nvapi.dll"; 367 | } 368 | else 369 | { 370 | return "nvapi64.dll"; 371 | } 372 | } 373 | 374 | private static void GetDelegate(uint id, out T newDelegate) 375 | where T : class 376 | { 377 | IntPtr ptr = nvapi_QueryInterface(id); 378 | if (ptr != IntPtr.Zero) 379 | { 380 | newDelegate = 381 | Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T; 382 | } 383 | else 384 | { 385 | newDelegate = null; 386 | } 387 | } 388 | 389 | static NVAPI() 390 | { 391 | DllImportAttribute attribute = new DllImportAttribute(GetDllName()); 392 | attribute.CallingConvention = CallingConvention.Cdecl; 393 | attribute.PreserveSig = true; 394 | attribute.EntryPoint = "nvapi_QueryInterface"; 395 | PInvokeDelegateFactory.CreateDelegate(attribute, 396 | out nvapi_QueryInterface); 397 | 398 | try 399 | { 400 | GetDelegate(0x0150E828, out NvAPI_Initialize); 401 | } 402 | catch (DllNotFoundException) { return; } 403 | catch (EntryPointNotFoundException) { return; } 404 | catch (ArgumentNullException) { return; } 405 | 406 | if (NvAPI_Initialize() == NvStatus.OK) 407 | { 408 | GetDelegate(0xE3640A56, out NvAPI_GPU_GetThermalSettings); 409 | GetDelegate(0xCEEE8E9F, out _NvAPI_GPU_GetFullName); 410 | GetDelegate(0x9ABDD40D, out NvAPI_EnumNvidiaDisplayHandle); 411 | GetDelegate(0x34EF9506, out NvAPI_GetPhysicalGPUsFromDisplay); 412 | GetDelegate(0xE5AC921F, out NvAPI_EnumPhysicalGPUs); 413 | GetDelegate(0x5F608315, out NvAPI_GPU_GetTachReading); 414 | GetDelegate(0x1BD69F49, out NvAPI_GPU_GetAllClocks); 415 | GetDelegate(0x60DED2ED, out NvAPI_GPU_GetPStates); 416 | GetDelegate(0x189A1FDF, out NvAPI_GPU_GetUsages); 417 | GetDelegate(0xDA141340, out NvAPI_GPU_GetCoolerSettings); 418 | GetDelegate(0x891FA0AE, out NvAPI_GPU_SetCoolerLevels); 419 | GetDelegate(0x774AA982, out NvAPI_GPU_GetMemoryInfo); 420 | GetDelegate(0xF951A4D1, out NvAPI_GetDisplayDriverVersion); 421 | GetDelegate(0x01053FA5, out _NvAPI_GetInterfaceVersionString); 422 | GetDelegate(0x2DDFB66E, out NvAPI_GPU_GetPCIIdentifiers); 423 | 424 | available = true; 425 | } 426 | } 427 | 428 | public static bool IsAvailable 429 | { 430 | get { return available; } 431 | } 432 | 433 | } 434 | } 435 | -------------------------------------------------------------------------------- /MinerControl/Hardware/PInvokeDelegateFactory.cs: -------------------------------------------------------------------------------- 1 | // http://open-hardware-monitor.googlecode.com/svn/trunk/Hardware/ 2 | 3 | /* 4 | 5 | This Source Code Form is subject to the terms of the Mozilla Public 6 | License, v. 2.0. If a copy of the MPL was not distributed with this 7 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | 9 | Copyright (C) 2009-2012 Michael Möller 10 | 11 | */ 12 | 13 | using System; 14 | using System.Collections.Generic; 15 | using System.Reflection; 16 | using System.Reflection.Emit; 17 | using System.Runtime.InteropServices; 18 | //using OpenHardwareMonitor.Collections; 19 | 20 | namespace OpenHardwareMonitor.Hardware 21 | { 22 | 23 | internal static class PInvokeDelegateFactory 24 | { 25 | 26 | private static readonly ModuleBuilder moduleBuilder = 27 | AppDomain.CurrentDomain.DefineDynamicAssembly( 28 | new AssemblyName("PInvokeDelegateFactoryInternalAssembly"), 29 | AssemblyBuilderAccess.Run).DefineDynamicModule( 30 | "PInvokeDelegateFactoryInternalModule"); 31 | 32 | //private static readonly IDictionary, Type> wrapperTypes = 33 | // new Dictionary, Type>(); 34 | private static readonly IDictionary, Type> wrapperTypes = 35 | new Dictionary, Type>(); 36 | 37 | public static void CreateDelegate(DllImportAttribute dllImportAttribute, 38 | out T newDelegate) where T : class 39 | { 40 | Type wrapperType; 41 | //Pair key = 42 | // new Pair(dllImportAttribute, typeof(T)); 43 | Tuple key = 44 | new Tuple(dllImportAttribute, typeof(T)); 45 | wrapperTypes.TryGetValue(key, out wrapperType); 46 | 47 | if (wrapperType == null) 48 | { 49 | wrapperType = CreateWrapperType(typeof(T), dllImportAttribute); 50 | wrapperTypes.Add(key, wrapperType); 51 | } 52 | 53 | newDelegate = Delegate.CreateDelegate(typeof(T), wrapperType, 54 | dllImportAttribute.EntryPoint) as T; 55 | } 56 | 57 | 58 | private static Type CreateWrapperType(Type delegateType, 59 | DllImportAttribute dllImportAttribute) 60 | { 61 | 62 | TypeBuilder typeBuilder = moduleBuilder.DefineType( 63 | "PInvokeDelegateFactoryInternalWrapperType" + wrapperTypes.Count); 64 | 65 | MethodInfo methodInfo = delegateType.GetMethod("Invoke"); 66 | 67 | ParameterInfo[] parameterInfos = methodInfo.GetParameters(); 68 | int parameterCount = parameterInfos.GetLength(0); 69 | 70 | Type[] parameterTypes = new Type[parameterCount]; 71 | for (int i = 0; i < parameterCount; i++) 72 | parameterTypes[i] = parameterInfos[i].ParameterType; 73 | 74 | MethodBuilder methodBuilder = typeBuilder.DefinePInvokeMethod( 75 | dllImportAttribute.EntryPoint, dllImportAttribute.Value, 76 | MethodAttributes.Public | MethodAttributes.Static | 77 | MethodAttributes.PinvokeImpl, CallingConventions.Standard, 78 | methodInfo.ReturnType, parameterTypes, 79 | dllImportAttribute.CallingConvention, 80 | dllImportAttribute.CharSet); 81 | 82 | foreach (ParameterInfo parameterInfo in parameterInfos) 83 | methodBuilder.DefineParameter(parameterInfo.Position + 1, 84 | parameterInfo.Attributes, parameterInfo.Name); 85 | 86 | if (dllImportAttribute.PreserveSig) 87 | methodBuilder.SetImplementationFlags(MethodImplAttributes.PreserveSig); 88 | 89 | return typeBuilder.CreateType(); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /MinerControl/MainWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Reflection; 8 | using System.Windows.Forms; 9 | using MinerControl.PriceEntries; 10 | using MinerControl.Services; 11 | using MinerControl.Utility; 12 | 13 | namespace MinerControl 14 | { 15 | public partial class MainWindow : Form 16 | { 17 | private MiningEngine _engine = new MiningEngine(); 18 | private DateTime AppStartTime = DateTime.Now; 19 | 20 | private bool IsMinimizedToTray 21 | { 22 | get { return _engine.TrayMode > 0 && this.WindowState == FormWindowState.Minimized; } 23 | } 24 | 25 | public MainWindow() 26 | { 27 | _engine.WriteConsoleAction = WriteConsole; 28 | _engine.WriteRemoteAction = WriteRemote; 29 | 30 | InitializeComponent(); 31 | } 32 | 33 | private void MainWindow_Load(object sender, EventArgs e) 34 | { 35 | if (!_engine.LoadConfig()) 36 | this.Close(); 37 | if (!string.IsNullOrWhiteSpace(_engine.CurrencyCode)) 38 | _engine.LoadExchangeRates(); 39 | } 40 | 41 | private void MainWindow_Shown(object sender, EventArgs e) 42 | { 43 | // speeds up data grid view performance. 44 | typeof(DataGridView).InvokeMember("DoubleBuffered", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, dgPrices, new object[] { true }); 45 | typeof(DataGridView).InvokeMember("DoubleBuffered", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, dgServices, new object[] { true }); 46 | 47 | dgServices.AutoGenerateColumns = false; 48 | dgServices.DataSource = new SortableBindingList(_engine.Services); 49 | 50 | dgPrices.AutoGenerateColumns = false; 51 | dgPrices.DataSource = new SortableBindingList(_engine.PriceEntries); 52 | 53 | if (!_engine.DoDonationMinging) 54 | { 55 | textDonationStart.Enabled = false; 56 | textDonationEnd.Enabled = false; 57 | } 58 | 59 | lblCurrencySymbol.Text = string.Empty; // Avoid flashing template value when starting 60 | 61 | if (!_engine.RemoteReceive) 62 | tabPage.TabPages.Remove(tabRemote); 63 | 64 | UpdateButtons(); 65 | RunCycle(); 66 | UpdateGrid(true); 67 | 68 | if (Program.MinimizeOnStart) 69 | MinimizeWindow(); 70 | 71 | tmrPriceCheck.Enabled = true; 72 | if (!string.IsNullOrWhiteSpace(_engine.CurrencyCode)) 73 | tmrExchangeUpdate.Enabled = true; 74 | if (Program.HasAutoStart) 75 | { 76 | _engine.MiningMode = MiningModeEnum.Automatic; 77 | UpdateButtons(); 78 | RunBestAlgo(); 79 | } 80 | } 81 | 82 | private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) 83 | { 84 | _engine.Cleanup(); 85 | } 86 | 87 | #region Timer events 88 | 89 | private void tmrPriceCheck_Tick(object sender, EventArgs e) 90 | { 91 | RunCycle(); 92 | UpdateGrid(); 93 | } 94 | 95 | private void tmrExchangeUpdate_Tick(object sender, EventArgs e) 96 | { 97 | _engine.LoadExchangeRates(); 98 | } 99 | 100 | private void tmrTimeUpdate_Tick(object sender, EventArgs e) 101 | { 102 | UpdateTimes(); 103 | 104 | if (_engine.PricesUpdated) 105 | { 106 | UpdateGrid(); 107 | _engine.PricesUpdated = false; 108 | } 109 | 110 | var autoModes = new[] { MiningModeEnum.Automatic, MiningModeEnum.Donation }; 111 | if (!autoModes.Contains(_engine.MiningMode)) return; 112 | 113 | RunBestAlgo(); 114 | } 115 | 116 | #endregion 117 | 118 | private void RunCycle() 119 | { 120 | _engine.CheckPrices(); 121 | } 122 | 123 | private void RunBestAlgo() 124 | { 125 | if (!_engine.HasPrices || (Program.HasAutoStart && (DateTime.Now - AppStartTime).TotalSeconds < 3)) return; 126 | 127 | var oldCurrent = _engine.CurrentRunning; 128 | var oldNext = _engine.NextRun; 129 | _engine.RunBestAlgo(IsMinimizedToTray); 130 | if (_engine.CurrentRunning != oldCurrent || _engine.NextRun != oldNext) 131 | UpdateGrid(); 132 | } 133 | 134 | private void UpdateButtons() 135 | { 136 | btnStart.Enabled = _engine.MiningMode == MiningModeEnum.Stopped; 137 | btnStop.Enabled = _engine.MiningMode != MiningModeEnum.Stopped; 138 | dgPrices.Columns[dgPrices.Columns.Count - 2].Visible = _engine.MiningMode != MiningModeEnum.Stopped; // Status column 139 | dgPrices.Columns[dgPrices.Columns.Count - 1].Visible = _engine.MiningMode == MiningModeEnum.Stopped; // Action column 140 | } 141 | 142 | private void UpdateGrid(bool forceReorder = false) 143 | { 144 | lock (_engine) 145 | { 146 | // mode 2 == sort always, mode 1 == sort when running, mode 0 == sort never 147 | if (_engine.GridSortMode == 2 || (_engine.GridSortMode == 1 && (forceReorder || _engine.MiningMode == MiningModeEnum.Automatic))) 148 | { 149 | dgPrices.Sort(dgPrices.Columns["NetEarn"], ListSortDirection.Descending); 150 | } 151 | } 152 | } 153 | 154 | #region Buttons 155 | 156 | private void btnStart_Click(object sender, EventArgs e) 157 | { 158 | if (_engine.MiningMode != MiningModeEnum.Stopped) return; 159 | _engine.MiningMode = MiningModeEnum.Automatic; 160 | 161 | UpdateButtons(); 162 | RunBestAlgo(); 163 | UpdateGrid(); 164 | } 165 | 166 | private void btnStop_Click(object sender, EventArgs e) 167 | { 168 | if (_engine.MiningMode == MiningModeEnum.Stopped) return; 169 | _engine.MiningMode = MiningModeEnum.Stopped; 170 | 171 | UpdateButtons(); 172 | _engine.RequestStop(); 173 | UpdateGrid(); 174 | } 175 | 176 | private void dgPrices_CellContentClick(object sender, DataGridViewCellEventArgs e) 177 | { 178 | if (_engine.MiningMode != MiningModeEnum.Stopped) return; 179 | 180 | var senderGrid = (DataGridView)sender; 181 | 182 | if (senderGrid.Columns[e.ColumnIndex] is DataGridViewButtonColumn && e.RowIndex >= 0) 183 | { 184 | var data = senderGrid.DataSource as IList; 185 | var entry = data[e.RowIndex]; 186 | 187 | _engine.MiningMode = MiningModeEnum.Manual; 188 | UpdateButtons(); 189 | _engine.RequestStart(entry.Id, IsMinimizedToTray); 190 | UpdateGrid(); 191 | } 192 | } 193 | 194 | #endregion 195 | 196 | private void linkDonate_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 197 | { 198 | Process.Start("https://blockchain.info/address/1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y"); 199 | } 200 | 201 | #region Show/hide window 202 | 203 | private void MinimizeWindow() 204 | { 205 | if (_engine.TrayMode == 0) 206 | { 207 | this.WindowState = FormWindowState.Minimized; 208 | } 209 | else 210 | { 211 | HideWindow(); 212 | } 213 | } 214 | 215 | private void HideWindow() 216 | { 217 | notifyIcon.Visible = true; 218 | notifyIcon.ShowBalloonTip(500); 219 | this.Hide(); 220 | 221 | _engine.HideMinerWindow(); 222 | } 223 | 224 | private void notifyIcon_DoubleClick(object sender, EventArgs e) 225 | { 226 | notifyIcon.Visible = false; 227 | this.Show(); 228 | this.WindowState = FormWindowState.Normal; 229 | 230 | _engine.MinimizeMinerWindow(); 231 | } 232 | 233 | private void MainWindow_Resize(object sender, EventArgs e) 234 | { 235 | if (_engine.TrayMode > 0 && this.WindowState == FormWindowState.Minimized) 236 | { 237 | HideWindow(); 238 | } 239 | } 240 | 241 | #endregion 242 | 243 | private void UpdateTimes() 244 | { 245 | textRunningTotal.Text = _engine.TotalTime.FormatTime(); 246 | textTimeCurrent.Text = _engine.MiningTime.FormatTime(); 247 | textTimeSwitch.Text = _engine.NextRunTime.FormatTime(); 248 | textTimeRestart.Text = _engine.RestartTime.FormatTime(); 249 | textDonationStart.Text = _engine.TimeUntilDonation.FormatTime(); 250 | textDonationEnd.Text = _engine.TimeDuringDonation.FormatTime(); 251 | textCurrencyExchange.Text = _engine.Exchange.ToString("N2"); 252 | lblCurrencySymbol.Text = _engine.CurrencySymbol; 253 | if (_engine.Services != null) 254 | { 255 | var balance = _engine.Services.Select(o => o.Currency).Sum(); 256 | textCurrencyBalance.Text = balance.ToString("N4"); 257 | } 258 | } 259 | 260 | private string ActiveTime(PriceEntryBase priceEntry) 261 | { 262 | var time = priceEntry.TimeMining; 263 | if (_engine.CurrentRunning == priceEntry.Id && _engine.StartMining.HasValue) 264 | time += (DateTime.Now - _engine.StartMining.Value); 265 | return time.FormatTime(); 266 | } 267 | 268 | SlidingBuffer _consoleBuffer = new SlidingBuffer(200); 269 | 270 | private void WriteConsole(string text) 271 | { 272 | Invoke(new MethodInvoker( 273 | delegate 274 | { 275 | _consoleBuffer.Add(text); 276 | 277 | textConsole.Lines = _consoleBuffer.ToArray(); 278 | textConsole.Focus(); 279 | textConsole.SelectionStart = textConsole.Text.Length; 280 | textConsole.SelectionLength = 0; 281 | textConsole.ScrollToCaret(); 282 | textConsole.Refresh(); 283 | } 284 | )); 285 | } 286 | 287 | SlidingBuffer _remoteBuffer = new SlidingBuffer(200); 288 | 289 | private void WriteRemote(IPAddress source, string text) 290 | { 291 | Invoke(new MethodInvoker( 292 | delegate 293 | { 294 | _remoteBuffer.Add(string.Format("[{0}] {1}", source, text)); 295 | 296 | textRemote.Lines = _remoteBuffer.ToArray(); 297 | textRemote.Focus(); 298 | textRemote.SelectionStart = textRemote.Text.Length; 299 | textRemote.SelectionLength = 0; 300 | textRemote.ScrollToCaret(); 301 | textRemote.Refresh(); 302 | } 303 | )); 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /MinerControl/MinerControl.conf: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "power": 0.10, 4 | "exchange": 500, 5 | "currencycode": "USD", 6 | "mintime": 4, 7 | "maxtime": 30, 8 | "switchtime": 3, 9 | "deadtime": 10, 10 | "logerrors": true, 11 | "logactivity": true, 12 | "gridsortmode": 1, 13 | "minerkillmode": 1, 14 | "traymode": 1, 15 | "donationpercentage": 2, 16 | "donationfrequency": 240, 17 | "remotesend": true, 18 | "remotereceive": true 19 | }, 20 | "algorithms": [ 21 | { "name": "x11", "display": "X11", "hashrate": 5860, "power": 49, "aparam1": "", "aparam2": "TestMiner.exe", "aparam3": "-a x11" }, 22 | { "name": "x13", "display": "X13", "hashrate": 4700, "power": 49, "aparam1": "", "aparam2": "TestMiner.exe", "aparam3": "-a x13" }, 23 | { "name": "x14", "display": "X14", "hashrate": 4573, "power": 52, "aparam1": "", "aparam2": "TestMiner.exe", "aparam3": "-a x14" }, 24 | { "name": "x15", "display": "X15", "hashrate": 4097, "power": 52, "aparam1": "", "aparam2": "TestMiner.exe", "aparam3": "-a x15" }, 25 | { "name": "quark", "display": "Quark", "hashrate": 10548, "power": 54, "aparam1": "", "aparam2": "TestMiner.exe", "aparam3": "-a quark" }, 26 | { "name": "nist5", "display": "Nist5", "hashrate": 17533, "power": 54, "aparam1": "", "aparam2": "TestMiner.exe", "aparam3": "-a nist5" }, 27 | { "name": "neoscrypt", "display": "NeoScrypt", "hashrate": 80, "power": 54, "aparam1": "", "aparam2": "TestMiner.exe", "aparam3": "-a neoscrypt" }, 28 | { "name": "scrypt", "display": "Scrypt", "hashrate": 540, "power": 60, "aparam1": "c:\\windows\\system32", "aparam2": "cmd.exe", "aparam3": "/c test-run.bat -a scrypt -batch" }, 29 | { "name": "scryptn", "display": "Scrypt-N", "hashrate": 253, "power": 60, "aparam1": "c:\\windows\\system32", "aparam2": "cmd.exe", "aparam3": "/c test-run.bat -a scryptn -batch" }, 30 | { "name": "keccak", "display": "Keccak", "hashrate": 301500, "power": 50, "aparam1": "", "aparam2": "TestMiner.exe", "aparam3": "-a keccak" }, 31 | { "name": "qubit", "display": "Qubit", "hashrate": 7500, "power": 50, "aparam1": "", "aparam2": "TestMiner.exe", "aparam3": "-a qubit" }, 32 | { "name": "lyra2", "display": "Lyra2RE", "hashrate": 1401, "power": 50, "aparam1": "", "aparam2": "TestMiner.exe", "aparam3": "-a lyra2re" }, 33 | { "name": "sha256", "display": "SHA256", "hashrate": 10000, "power": 50, "aparam1": "c:\\windows\\system32", "aparam2": "cmd.exe", "aparam3": "/c test-run.bat -a sha256 -batch" } 34 | ], 35 | "nicehash": { 36 | "account": "1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y", 37 | "worker": "1", 38 | "sparam1": "-o stratum+tcp://stratum.nicehash.com", 39 | "sparam2": "-p x", 40 | "weight": 0.90, 41 | "algos": [ 42 | { "algo": "x11", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3336 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 43 | { "algo": "x13", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3337 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 44 | { "algo": "x15", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3339 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 45 | { "algo": "scrypt", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3333 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 46 | { "algo": "scryptn", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3335 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 47 | { "algo": "keccak", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3338 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 48 | { "algo": "nist5", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3340 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 49 | { "algo": "neoscrypt", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3341 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 50 | { "algo": "lyra2", "priceid": "9", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3342 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 51 | { "algo": "sha256", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3332 -u _ACCOUNT_._WORKER_ _SPARAM2_", "usewindow": true } 52 | ] 53 | }, 54 | "westhash": { 55 | "account": "1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y", 56 | "worker": "1", 57 | "sparam1": "-o stratum+tcp://stratum.westhash.com", 58 | "sparam2": "-p x", 59 | "algos": [ 60 | { "algo": "x11", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3336 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 61 | { "algo": "x13", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3337 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 62 | { "algo": "x15", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3339 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 63 | { "algo": "scrypt", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3333 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 64 | { "algo": "scryptn", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3335 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 65 | { "algo": "keccak", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3338 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 66 | { "algo": "nist5", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3340 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 67 | { "algo": "lyra2", "priceid": "9", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3342 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 68 | { "algo": "neoscrypt", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3341 -u _ACCOUNT_._WORKER_ _SPARAM2_" } 69 | ] 70 | }, 71 | "yaamp": { 72 | "account": "1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y", 73 | "pricemode": 1, 74 | "sparam1": "-o stratum+tcp://yaamp.com", 75 | "sparam2": "-p x", 76 | "algos": [ 77 | { "algo": "x11", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3533 -u _ACCOUNT_ _SPARAM2_" }, 78 | { "algo": "x13", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3633 -u _ACCOUNT_ _SPARAM2_" }, 79 | { "algo": "x14", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3933 -u _ACCOUNT_ _SPARAM2_" }, 80 | { "algo": "x15", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3733 -u _ACCOUNT_ _SPARAM2_" }, 81 | { "algo": "quark", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:4033 -u _ACCOUNT_ _SPARAM2_" }, 82 | { "algo": "nist5", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3833 -u _ACCOUNT_ _SPARAM2_" }, 83 | { "algo": "lyra2", "priceid": "lyra2", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:4433 -u _ACCOUNT_ _SPARAM2_" }, 84 | { "algo": "scrypt", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3433 -u _ACCOUNT_ _SPARAM2_" } 85 | ] 86 | }, 87 | "wafflepool": { 88 | "account": "1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y", 89 | "worker": "1", 90 | "sparam1": "-o stratum+tcp://useast.wafflepool.com", 91 | "sparam2": "-p x", 92 | "algos": [ 93 | { "algo": "x11", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3331 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 94 | { "algo": "x13", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3330 _ACCOUNT_._WORKER_ _SPARAM2_" }, 95 | { "algo": "scrypt", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3333 _ACCOUNT_._WORKER_ _SPARAM2_" }, 96 | { "algo": "scryptn", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3332 _ACCOUNT_._WORKER_ _SPARAM2_" } 97 | ] 98 | }, 99 | "ltcrabbit": { 100 | "apikey": "mykey", 101 | "account": "MinerControl", 102 | "worker": "1", 103 | "sparam2": "-p x", 104 | "algos": [ 105 | { "algo": "x11", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ -o stratum+tcp://x11.ltcrabbit.com:3332 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 106 | { "algo": "scrypt", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ -o stratum+tcp://scrypt.ltcrabbit.com:3333 _ACCOUNT_._WORKER_ _SPARAM2_" } 107 | ] 108 | }, 109 | "wepaybtc": { 110 | "account": "1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y", 111 | "worker": "1", 112 | "sparam1": "-o ny", 113 | "sparam2": "-p MinerControl@StuffOfInterest.com", 114 | "algos": [ 115 | { "algo": "x11", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_ -u _ACCOUNT___WORKER_ _SPARAM2_" }, 116 | { "algo": "x13", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_ -u _ACCOUNT___WORKER_ _SPARAM2_" }, 117 | { "algo": "x15", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_ -u _ACCOUNT___WORKER_ _SPARAM2_" }, 118 | { "algo": "nist5", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_ -u _ACCOUNT___WORKER_ _SPARAM2_" } 119 | ] 120 | }, 121 | "hamsterpool": { 122 | "apikey": "mykey", 123 | "donation": 1.0, 124 | "account": "MinerControl", 125 | "worker": "1", 126 | "sparam1": "-o stratum+tcp://eu.hamsterpool.com", 127 | "sparam2": "-p x", 128 | "algos": [ 129 | { "algo": "scrypt", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:7771 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 130 | { "algo": "scryptn", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:7772 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 131 | { "algo": "sha256", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:7774 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 132 | { "algo": "x11", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3508 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 133 | { "algo": "qubit", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:7776 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 134 | { "algo": "x13", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:7773 -u _ACCOUNT_._WORKER_ _SPARAM2_" }, 135 | { "algo": "neoscrypt", "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ _SPARAM1_:3650 -u _ACCOUNT_._WORKER_ _SPARAM2_" } 136 | ] 137 | }, 138 | "manual": { 139 | "account": "myaccount", 140 | "algos": [ 141 | { "algo": "scrypt", "price": 0.0100, "fee": 3.0, "folder": "_APARAM1_", "command": "_APARAM2_", "arguments": "_APARAM3_ -o manual.com:1 _ACCOUNT_ -p x" } 142 | ] 143 | } 144 | } -------------------------------------------------------------------------------- /MinerControl/MinerControl.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {94CC3F87-4215-4F3F-9524-695BF16FEBD0} 8 | WinExe 9 | Properties 10 | MinerControl 11 | MinerControl 12 | v4.0 13 | 512 14 | 15 | 16 | 17 | x86 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | bitcoin.ico 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | Form 66 | 67 | 68 | MainWindow.cs 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | MainWindow.cs 100 | 101 | 102 | Always 103 | 104 | 105 | Always 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | {63721d21-b76a-40ae-9a37-0c6f392cee63} 117 | TestMiner 118 | 119 | 120 | 121 | 128 | -------------------------------------------------------------------------------- /MinerControl/MiningEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Threading; 8 | using System.Web.Script.Serialization; 9 | using System.Windows.Forms; 10 | using MinerControl.PriceEntries; 11 | using MinerControl.Services; 12 | using MinerControl.Utility; 13 | using MinerControl.Utility.Multicast; 14 | 15 | namespace MinerControl 16 | { 17 | public class MiningEngine 18 | { 19 | private const int RemotePortNumber = 12814; 20 | 21 | private Process _process; 22 | private IList _algorithmEntries = new List(); 23 | private IList _priceEntries = new List(); 24 | private IList _services = new List(); 25 | private decimal _powerCost; 26 | private decimal _exchange; 27 | private string _currencyCode; 28 | private string _currencySymbol; 29 | private bool _logactivity; 30 | private PriceEntryBase _currentRunning; 31 | private DateTime? _startMining; 32 | private TimeSpan _minTime; 33 | private TimeSpan _maxTime; 34 | private TimeSpan _switchTime; 35 | private TimeSpan _deadtime; 36 | private int? _nextRun; // Next algo to run 37 | private DateTime? _nextRunFromTime; // When the next run algo became best 38 | 39 | public MiningModeEnum MiningMode { get; set; } 40 | 41 | public int MinerKillMode { get; private set; } 42 | public int GridSortMode { get; private set; } 43 | public int TrayMode { get; private set; } // How to handle minimizing ot the tray 44 | 45 | public int? CurrentRunning { get { return _currentRunning == null ? (int?)null : _currentRunning.Id; } } 46 | public int? NextRun { get { return _nextRun; } } 47 | public DateTime? StartMining { get { return _startMining; } } 48 | public decimal PowerCost { get { return _powerCost; } } 49 | public decimal Exchange { get { return _exchange; } } 50 | public string CurrencyCode { get { return _currencyCode; } } 51 | public string CurrencySymbol { get { return _currencySymbol; } } 52 | public TimeSpan DeadTime { get { return _deadtime; } } 53 | 54 | public TimeSpan? RestartTime 55 | { 56 | get 57 | { 58 | return _startMining.HasValue && _maxTime > TimeSpan.Zero 59 | ? _maxTime - (DateTime.Now - _startMining.Value) 60 | : (TimeSpan?)null; 61 | } 62 | } 63 | 64 | public TimeSpan? MiningTime 65 | { 66 | get 67 | { 68 | return _startMining.HasValue 69 | ? DateTime.Now - _startMining.Value 70 | : (TimeSpan?)null; 71 | } 72 | } 73 | 74 | // How long until next run starts 75 | public TimeSpan? NextRunTime 76 | { 77 | get 78 | { 79 | if (_nextRun == null || _nextRunFromTime == null || _startMining == null) 80 | return (TimeSpan?)null; 81 | 82 | var timeToSwitch = _switchTime - (DateTime.Now - _nextRunFromTime); 83 | var timeToMin = _minTime - (DateTime.Now - _startMining); 84 | 85 | return timeToMin > timeToSwitch ? timeToMin : timeToSwitch; 86 | } 87 | } 88 | 89 | public IList Services { get { return _services; } } 90 | public IList PriceEntries { get { return _priceEntries; } } 91 | public IList AlgorithmEntries { get { return _algorithmEntries; } } 92 | 93 | public TimeSpan TotalTime 94 | { 95 | get 96 | { 97 | var totalTime = PriceEntries.Sum(o => o.TimeMining.TotalMilliseconds); 98 | if (_startMining.HasValue) 99 | totalTime += (DateTime.Now - _startMining.Value).TotalMilliseconds; 100 | return TimeSpan.FromMilliseconds(totalTime); 101 | } 102 | } 103 | 104 | // Signals for UI updates 105 | private volatile bool _pricesUpdated; 106 | private volatile bool _hasPrices; 107 | public bool PricesUpdated { get { return _pricesUpdated; } set { _pricesUpdated = value; } } 108 | public bool HasPrices { get { return _hasPrices; } set { _hasPrices = value; } } 109 | 110 | #region Donation mining settings 111 | 112 | private double _donationpercentage = 0.02; 113 | public bool DoDonationMinging { get { return _donationpercentage > 0 && _donationfrequency > TimeSpan.Zero; } } 114 | private TimeSpan _donationfrequency = TimeSpan.FromMinutes(240); 115 | private TimeSpan _autoMiningTime = TimeSpan.Zero; 116 | private TimeSpan _donationMiningTime = TimeSpan.Zero; 117 | private MiningModeEnum _donationMiningMode = MiningModeEnum.Stopped; 118 | 119 | private TimeSpan MiningBeforeDonation 120 | { 121 | get 122 | { 123 | if (!DoDonationMinging) return TimeSpan.Zero; 124 | return TimeSpan.FromMinutes(_donationfrequency.TotalMinutes * (1 - _donationpercentage)); 125 | } 126 | } 127 | 128 | private TimeSpan MiningDuringDonation 129 | { 130 | get 131 | { 132 | if (!DoDonationMinging) return TimeSpan.Zero; 133 | return _donationfrequency - MiningBeforeDonation; 134 | } 135 | } 136 | 137 | public TimeSpan TimeUntilDonation 138 | { 139 | get 140 | { 141 | if (!DoDonationMinging) return TimeSpan.Zero; 142 | var miningTime = _autoMiningTime; 143 | if (MiningMode == MiningModeEnum.Automatic && _startMining.HasValue) miningTime += (DateTime.Now - _startMining.Value); 144 | var value = MiningBeforeDonation - miningTime; 145 | return value < TimeSpan.Zero ? TimeSpan.Zero : value; 146 | } 147 | } 148 | 149 | public TimeSpan TimeDuringDonation 150 | { 151 | get 152 | { 153 | if (!DoDonationMinging) return TimeSpan.Zero; 154 | var miningTime = _donationMiningTime; 155 | if (MiningMode == MiningModeEnum.Donation && _startMining.HasValue) miningTime += (DateTime.Now - _startMining.Value); 156 | var value = MiningDuringDonation - miningTime; 157 | return value < TimeSpan.Zero ? TimeSpan.Zero : value; 158 | } 159 | } 160 | 161 | #endregion 162 | 163 | #region Remote console 164 | 165 | private bool _remoteSend; 166 | private bool _remoteReceive; 167 | private IPEndPoint _endPoint = new IPEndPoint(new IPAddress(new byte[] { 239, 14, 10, 30 }), RemotePortNumber); 168 | private MulticastSender _remoteSender; 169 | private MulticastReceiver _remoteReceiver; 170 | 171 | public bool RemoteReceive { get { return _remoteReceive; } } 172 | 173 | #endregion 174 | 175 | public MiningEngine() 176 | { 177 | MinerKillMode = 1; 178 | GridSortMode = 1; 179 | MiningMode = MiningModeEnum.Stopped; 180 | } 181 | 182 | public void Cleanup() 183 | { 184 | WriteConsoleAction = null; 185 | 186 | if (_currentRunning != null && _currentRunning.UseWindow == false) 187 | StopMiner(); 188 | 189 | if (_process != null) 190 | _process.Dispose(); 191 | 192 | if (_remoteSender != null) 193 | _remoteSender.Dispose(); 194 | 195 | if (_remoteReceiver != null) 196 | _remoteReceiver.Dispose(); 197 | } 198 | 199 | public bool LoadConfig() 200 | { 201 | const string configFile = "MinerControl.conf"; 202 | if (!File.Exists(configFile)) 203 | { 204 | MessageBox.Show(string.Format("Config file, '{0}', not found.", configFile), "Miner Control: Config file missing", MessageBoxButtons.OK, MessageBoxIcon.Error); 205 | return false; 206 | } 207 | 208 | Dictionary data; 209 | 210 | try 211 | { 212 | var pageString = File.ReadAllText(configFile); 213 | var serializer = new JavaScriptSerializer(); 214 | data = serializer.DeserializeObject(pageString) as Dictionary; 215 | } 216 | catch (ArgumentException ex) 217 | { 218 | MessageBox.Show(string.Format("Error loading config file: '{0}'.", ex.Message), "Miner Control: Config file error", MessageBoxButtons.OK, MessageBoxIcon.Error); 219 | return false; 220 | } 221 | 222 | try 223 | { 224 | LoadConfigGeneral(data["general"] as Dictionary); 225 | LoadConfigAlgorithms(data["algorithms"] as object[]); 226 | } 227 | catch (Exception ex) 228 | { 229 | MessageBox.Show(string.Format("Error processing general configuration: '{0}'.", ex.Message), "Miner Control: Config file error", MessageBoxButtons.OK, MessageBoxIcon.Error); 230 | return false; 231 | } 232 | 233 | try 234 | { 235 | LoadService(new NiceHashService(), data, "nicehash"); 236 | LoadService(new WestHashService(), data, "westhash"); 237 | LoadService(new TradeMyBitService(), data, "trademybit"); 238 | LoadService(new YaampService(), data, "yaamp"); 239 | LoadService(new WafflePoolService(), data, "wafflepool"); 240 | LoadService(new LtcRabbitService(), data, "ltcrabbit"); 241 | LoadService(new WePayBtcService(), data, "wepaybtc"); 242 | LoadService(new HamsterPoolService(), data, "hamsterpool"); 243 | LoadService(new ManualService(), data, "manual"); 244 | 245 | // Set Id for each entry 246 | for (var x = 0; x < _priceEntries.Count; x++) 247 | _priceEntries[x].Id = x + 1; 248 | } 249 | catch (Exception ex) 250 | { 251 | MessageBox.Show(string.Format("Error processing service configuration: '{0}'.", ex.Message), "Miner Control: Configuration file error", MessageBoxButtons.OK, MessageBoxIcon.Error); 252 | return false; 253 | } 254 | 255 | if (_remoteSend) 256 | { 257 | _remoteSender = new MulticastSender(_endPoint, 1); 258 | _remoteSender.Start(); 259 | } 260 | 261 | if (_remoteReceive) 262 | { 263 | _remoteReceiver = new MulticastReceiver(_endPoint); 264 | _remoteReceiver.DataReceived += ProcessRemoteData; 265 | _remoteReceiver.Start(); 266 | } 267 | 268 | return true; 269 | } 270 | 271 | private void LoadService(IService service, IDictionary data, string name) 272 | { 273 | if (!data.ContainsKey(name)) return; 274 | service.MiningEngine = this; 275 | _services.Add(service); 276 | service.Initialize(data[name] as Dictionary); 277 | } 278 | 279 | private void LoadConfigGeneral(IDictionary data) 280 | { 281 | _powerCost = data["power"].ExtractDecimal(); 282 | _exchange = data["exchange"].ExtractDecimal(); 283 | if (data.ContainsKey("currencycode")) 284 | _currencyCode = data["currencycode"].ToString().ToUpper(); 285 | _minTime = TimeSpan.FromMinutes((double)data["mintime"].ExtractDecimal()); 286 | _maxTime = TimeSpan.FromMinutes((double)data["maxtime"].ExtractDecimal()); 287 | _switchTime = TimeSpan.FromMinutes((double)data["switchtime"].ExtractDecimal()); 288 | _deadtime = TimeSpan.FromMinutes((double)data["deadtime"].ExtractDecimal()); 289 | 290 | if (data.ContainsKey("logerrors")) 291 | ErrorLogger.LogExceptions = bool.Parse(data["logerrors"].ToString()); 292 | if (data.ContainsKey("logactivity")) 293 | _logactivity = bool.Parse(data["logactivity"].ToString()); 294 | if (data.ContainsKey("minerkillmode")) 295 | MinerKillMode = int.Parse(data["minerkillmode"].ToString()); 296 | if (data.ContainsKey("gridsortmode")) 297 | GridSortMode = int.Parse(data["gridsortmode"].ToString()); 298 | if (data.ContainsKey("traymode")) 299 | TrayMode = int.Parse(data["traymode"].ToString()); 300 | if (Program.MinimizeToTray && TrayMode == 0) 301 | TrayMode = 2; 302 | if (data.ContainsKey("donationpercentage")) 303 | _donationpercentage = (double)(data["donationpercentage"].ExtractDecimal()) / 100; 304 | if (data.ContainsKey("donationfrequency")) 305 | _donationfrequency = TimeSpan.FromMinutes((double)data["donationfrequency"].ExtractDecimal()); 306 | if (data.ContainsKey("remotesend")) 307 | _remoteSend = bool.Parse(data["remotesend"].ToString()); 308 | if (data.ContainsKey("remotereceive")) 309 | _remoteReceive = bool.Parse(data["remotereceive"].ToString()); 310 | } 311 | 312 | private void LoadConfigAlgorithms(object[] data) 313 | { 314 | foreach (var rawitem in data) 315 | { 316 | var item = rawitem as Dictionary; 317 | var entry = new AlgorithmEntry 318 | { 319 | Name = item["name"] as string, 320 | Display = item.ContainsKey("display") ? item["display"] as string : GetAlgoDisplayName(item["name"] as string), 321 | Hashrate = item["hashrate"].ExtractDecimal(), 322 | Power = item["power"].ExtractDecimal(), 323 | Param1 = item.GetString("aparam1") ?? string.Empty, 324 | Param2 = item.GetString("aparam2") as string ?? string.Empty, 325 | Param3 = item.GetString("aparam3") as string ?? string.Empty 326 | }; 327 | 328 | _algorithmEntries.Add(entry); 329 | } 330 | } 331 | 332 | public void StopMiner() 333 | { 334 | if (!_process.IsRunning()) 335 | { 336 | _process = null; 337 | return; 338 | } 339 | 340 | LogActivity(_donationMiningMode == MiningModeEnum.Donation ? "DonationStop" : "Stop"); 341 | WriteConsole(string.Format("Stopping {0} {1}", _currentRunning.ServicePrint, _currentRunning.AlgoName), true); 342 | RecordMiningTime(); 343 | if (MinerKillMode == 0) 344 | ProcessUtil.KillProcess(_process); 345 | else 346 | ProcessUtil.KillProcessAndChildren(_process.Id); 347 | 348 | _process = null; 349 | _donationMiningMode = MiningModeEnum.Stopped; 350 | 351 | if (_currentRunning != null) 352 | { 353 | var entry = PriceEntries.Where(o => o.Id == _currentRunning.Id).Single(); 354 | entry.UpdateStatus(); 355 | } 356 | 357 | _currentRunning = null; 358 | } 359 | 360 | private void RecordMiningTime() 361 | { 362 | if (_currentRunning == null || !_startMining.HasValue) return; 363 | 364 | if (_donationMiningMode == MiningModeEnum.Automatic) _autoMiningTime += (DateTime.Now - _startMining.Value); 365 | if (_donationMiningMode == MiningModeEnum.Donation) _donationMiningTime += (DateTime.Now - _startMining.Value); 366 | 367 | _currentRunning.TimeMining += (DateTime.Now - _startMining.Value); 368 | _currentRunning.UpdateStatus(); 369 | _startMining = null; 370 | } 371 | 372 | private void StartMiner(PriceEntryBase entry, bool isMinimizedToTray = false) 373 | { 374 | _nextRun = null; 375 | _nextRunFromTime = null; 376 | _currentRunning = entry; 377 | _startMining = DateTime.Now; 378 | 379 | _process = new Process(); 380 | if (_donationMiningMode == MiningModeEnum.Donation) 381 | { 382 | if (!string.IsNullOrWhiteSpace(entry.DonationFolder)) 383 | _process.StartInfo.WorkingDirectory = entry.DonationFolder; 384 | _process.StartInfo.FileName = string.IsNullOrWhiteSpace(entry.DonationFolder) 385 | ? entry.DonationCommand 386 | : string.Format(@"{0}\{1}", entry.DonationFolder, entry.DonationCommand); 387 | _process.StartInfo.Arguments = entry.DonationArguments; 388 | } 389 | else 390 | { 391 | if (!string.IsNullOrWhiteSpace(entry.Folder)) 392 | _process.StartInfo.WorkingDirectory = entry.Folder; 393 | _process.StartInfo.FileName = string.IsNullOrWhiteSpace(entry.Folder) 394 | ? entry.Command 395 | : string.Format(@"{0}\{1}", entry.Folder, entry.Command); 396 | _process.StartInfo.Arguments = entry.Arguments; 397 | } 398 | 399 | WriteConsole(string.Format("Starting {0} {1} with {2} {3}", _currentRunning.ServicePrint, _currentRunning.Name, _process.StartInfo.FileName, _process.StartInfo.Arguments), true); 400 | 401 | if (!string.IsNullOrWhiteSpace(_process.StartInfo.WorkingDirectory) && !Directory.Exists(_process.StartInfo.WorkingDirectory)) 402 | { 403 | entry.DeadTime = DateTime.Now; 404 | var message = string.Format("Path '{0}' does not exist.", _process.StartInfo.WorkingDirectory); 405 | _process = null; 406 | WriteConsole(message, true); 407 | throw new ArgumentException(message); 408 | } 409 | if (!string.IsNullOrWhiteSpace(_process.StartInfo.FileName) && !File.Exists(_process.StartInfo.FileName)) 410 | { 411 | entry.DeadTime = DateTime.Now; 412 | var message = string.Format("File '{0}' does not exist.", _process.StartInfo.FileName); 413 | _process = null; 414 | WriteConsole(message, true); 415 | throw new ArgumentException(message); 416 | } 417 | 418 | if (entry.UseWindow) 419 | { 420 | _process.StartInfo.WindowStyle = (isMinimizedToTray && TrayMode == 2) ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Minimized; 421 | _process.Start(); 422 | 423 | Thread.Sleep(100); 424 | try 425 | { 426 | ProcessUtil.SetWindowTitle(_process, string.Format("{0} {1} Miner", entry.ServicePrint, entry.Name)); 427 | } 428 | catch (Exception ex) 429 | { 430 | ErrorLogger.Log(ex); 431 | } 432 | 433 | if (isMinimizedToTray && TrayMode == 1) 434 | HideMinerWindow(); 435 | } 436 | else 437 | { 438 | _process.StartInfo.RedirectStandardOutput = true; 439 | _process.StartInfo.RedirectStandardError = true; 440 | _process.EnableRaisingEvents = true; 441 | _process.StartInfo.CreateNoWindow = true; 442 | _process.StartInfo.UseShellExecute = false; 443 | 444 | _process.ErrorDataReceived += ProcessConsoleOutput; 445 | _process.OutputDataReceived += ProcessConsoleOutput; 446 | 447 | _process.Start(); 448 | 449 | _process.BeginOutputReadLine(); 450 | _process.BeginErrorReadLine(); 451 | } 452 | 453 | _startMining = DateTime.Now; 454 | _donationMiningMode = MiningMode; 455 | 456 | entry.UpdateStatus(); 457 | 458 | LogActivity(_donationMiningMode == MiningModeEnum.Donation ? "DonationStart" : "Start"); 459 | } 460 | 461 | private void ClearDeadTimes() 462 | { 463 | foreach (var entry in _priceEntries) 464 | entry.DeadTime = DateTime.MinValue; 465 | } 466 | 467 | public void RequestStop() 468 | { 469 | _nextRun = null; 470 | _nextRunFromTime = null; 471 | 472 | StopMiner(); 473 | ClearDeadTimes(); 474 | } 475 | 476 | public void RequestStart(int id, bool isMinimizedToTray = false) 477 | { 478 | var entry = _priceEntries.Single(o => o.Id == id); 479 | StartMiner(entry, isMinimizedToTray); 480 | } 481 | 482 | public void CheckPrices() 483 | { 484 | foreach (var service in _services) 485 | service.CheckPrices(); 486 | } 487 | 488 | public void RunBestAlgo(bool isMinimizedToTray) 489 | { 490 | try 491 | { 492 | // Check for dead process 493 | if (!_process.IsRunning() && _currentRunning != null) 494 | { 495 | lock (this) 496 | { 497 | _currentRunning.DeadTime = DateTime.Now; 498 | LogActivity(_donationMiningMode == MiningModeEnum.Donation ? "DonationDead" : "Dead"); 499 | WriteConsole(string.Format("Dead {0} {1}", _currentRunning.ServicePrint, _currentRunning.Name), true); 500 | RecordMiningTime(); 501 | } 502 | } 503 | 504 | // Clear information if process not running 505 | if (_process == null || _process.HasExited) 506 | { 507 | _currentRunning = null; 508 | _startMining = null; 509 | _nextRun = null; 510 | _nextRunFromTime = null; 511 | } 512 | 513 | // Donation mining 514 | if (DoDonationMinging) 515 | { 516 | if (_donationMiningMode == MiningModeEnum.Automatic && TimeUntilDonation == TimeSpan.Zero) 517 | { 518 | StopMiner(); 519 | _donationMiningMode = MiningModeEnum.Donation; 520 | MiningMode = _donationMiningMode; 521 | _autoMiningTime = TimeSpan.Zero; 522 | } 523 | else if (_donationMiningMode == MiningModeEnum.Donation && TimeDuringDonation == TimeSpan.Zero) 524 | { 525 | StopMiner(); 526 | _donationMiningMode = MiningModeEnum.Automatic; 527 | MiningMode = _donationMiningMode; 528 | _donationMiningTime = TimeSpan.Zero; 529 | } 530 | } 531 | 532 | // Restart miner if max time reached 533 | if (RestartTime.HasValue && RestartTime.Value <= TimeSpan.Zero) 534 | StopMiner(); 535 | 536 | // Find the best, live entry 537 | var best = _donationMiningMode == MiningModeEnum.Donation 538 | ? _priceEntries 539 | .Where(o => !o.IsDead) 540 | .Where(o => !string.IsNullOrWhiteSpace(o.DonationCommand)) 541 | .OrderByDescending(o => o.NetEarn) 542 | .First() 543 | : _priceEntries 544 | .Where(o => !o.IsDead) 545 | .Where(o => !string.IsNullOrWhiteSpace(o.Command)) 546 | .OrderByDescending(o => o.NetEarn) 547 | .First(); 548 | 549 | // Handle minimum time for better algorithm before switching 550 | if (_switchTime > TimeSpan.Zero && _currentRunning != null) 551 | { 552 | if (!_nextRun.HasValue && _currentRunning.Id != best.Id) 553 | { 554 | _nextRun = best.Id; 555 | _nextRunFromTime = DateTime.Now; 556 | } 557 | else if (_nextRun.HasValue && _currentRunning.Id == best.Id) 558 | { 559 | _nextRun = null; 560 | _nextRunFromTime = null; 561 | } 562 | if (NextRunTime.HasValue && NextRunTime > TimeSpan.Zero) 563 | best = _priceEntries.First(o => o.Id == _currentRunning.Id); 564 | } 565 | 566 | // Update undead entries 567 | var entries = PriceEntries.Where(o => !o.IsDead && o.DeadTime != DateTime.MinValue); 568 | foreach (var entry in entries) 569 | entry.DeadTime = DateTime.MinValue; 570 | 571 | // Just update time if we are already running the right entry 572 | if (_currentRunning != null && _currentRunning.Id == best.Id) 573 | { 574 | _currentRunning.UpdateStatus(); 575 | return; 576 | } 577 | 578 | // Honor minimum time to run in auto mode 579 | if (MiningTime.HasValue && MiningTime.Value < _minTime) 580 | { 581 | _currentRunning.UpdateStatus(); 582 | return; 583 | } 584 | 585 | StopMiner(); 586 | StartMiner(best, isMinimizedToTray); 587 | } 588 | catch (Exception ex) 589 | { 590 | ErrorLogger.Log(ex); 591 | } 592 | } 593 | 594 | public void HideMinerWindow() 595 | { 596 | if (_currentRunning == null || !_currentRunning.UseWindow) return; 597 | 598 | ProcessUtil.HideWindow(_process); 599 | } 600 | 601 | public void MinimizeMinerWindow() 602 | { 603 | if (_currentRunning == null || !_currentRunning.UseWindow) return; 604 | 605 | ProcessUtil.MinimizeWindow(_process); 606 | } 607 | 608 | public void LoadExchangeRates() 609 | { 610 | WebUtil.DownloadJson("http://blockchain.info/ticker", ProcessExchangeRates); 611 | } 612 | 613 | private void ProcessExchangeRates(object jsonData) 614 | { 615 | var data = jsonData as Dictionary; 616 | if (!data.ContainsKey(_currencyCode)) return; 617 | var item = data[_currencyCode] as Dictionary; 618 | var exchange = item["last"].ExtractDecimal(); 619 | var symbol = item["symbol"].ToString(); 620 | lock (this) 621 | { 622 | if (exchange > 0 && exchange != _exchange) 623 | { 624 | _exchange = exchange; 625 | if (PriceEntries != null) 626 | foreach (var entry in PriceEntries) 627 | entry.UpdateExchange(); 628 | if (Services != null) 629 | foreach (var service in Services) 630 | service.UpdateExchange(); 631 | } 632 | 633 | if (!string.IsNullOrWhiteSpace(symbol)) _currencySymbol = symbol; 634 | } 635 | } 636 | 637 | private void LogActivity(string action) 638 | { 639 | if (!_logactivity) return; 640 | 641 | var items = new[] 642 | { 643 | DateTime.Now.ToString("s"), 644 | action, 645 | MiningMode.ToString(), 646 | _currentRunning != null ? _currentRunning.ServicePrint : string.Empty, 647 | _currentRunning != null ? _currentRunning.AlgoName : string.Empty, 648 | _currentRunning != null ? _currentRunning.Price.ToString("F6") : string.Empty, 649 | _currentRunning != null ? _currentRunning.Earn.ToString("F6") : string.Empty, 650 | _currentRunning != null ? _currentRunning.Fees.ToString("F6") : string.Empty, 651 | _currentRunning != null ? _currentRunning.Power.ToString("F6") : string.Empty, 652 | _currentRunning != null ? _currentRunning.NetEarn.ToString("F6") : string.Empty, 653 | _exchange.ToString("F2"), 654 | _currentRunning != null ? _currentRunning.ServiceEntry.Balance.ToString("F8") : string.Empty 655 | }; 656 | 657 | var line = string.Join(",", items); 658 | const string logfile = "activity.log"; 659 | 660 | var exists = File.Exists(logfile); 661 | using (var w = exists ? File.AppendText(logfile) : File.CreateText(logfile)) 662 | { 663 | if (!exists) 664 | w.WriteLine("time,action,mode,service,algo,price,earn,fees,power,netearn,exchange,servicebalance"); 665 | w.WriteLine(line); 666 | } 667 | } 668 | 669 | #region Console interaction 670 | 671 | public Action WriteConsoleAction { get; set; } 672 | 673 | private void WriteConsole(string text, bool prefixTime = false) 674 | { 675 | if (WriteConsoleAction == null) return; 676 | 677 | if (prefixTime) 678 | text = string.Format("[{0:HH:mm:ss}] {1}", DateTime.Now, text); 679 | 680 | WriteConsoleAction(text); 681 | 682 | if (_remoteSender == null) return; 683 | text = string.Format("{0} {1}", "CON", text); 684 | _remoteSender.Send(text); 685 | } 686 | 687 | private void ProcessConsoleOutput(object sender, DataReceivedEventArgs e) 688 | { 689 | if (e.Data == null) return; 690 | WriteConsole(e.Data); 691 | } 692 | 693 | public Action WriteRemoteAction { get; set; } 694 | 695 | private void ProcessRemoteData(object sender, MulticastDataReceivedEventArgs e) 696 | { 697 | if (WriteRemoteAction == null) return; 698 | 699 | try 700 | { 701 | var data = e.StringData; 702 | var command = data.Substring(0, 4); 703 | var body = data.Substring(4); 704 | 705 | switch (command) 706 | { 707 | case "CON ": 708 | WriteRemoteAction(e.RemoteEndPoint.Address, body); 709 | break; 710 | } 711 | } 712 | catch (Exception ex) 713 | { 714 | ErrorLogger.Log(ex); 715 | } 716 | } 717 | 718 | #endregion 719 | 720 | #region Helpers 721 | 722 | private readonly IDictionary _algoNames = new Dictionary 723 | { 724 | {"x11", "X11"}, 725 | {"x13", "X13"}, 726 | {"x14", "X14"}, 727 | {"x15", "X15"}, 728 | {"scrypt", "Scrypt"}, 729 | {"scryptn", "Scrypt-N"}, 730 | {"sha256", "SHA256"}, 731 | {"nist5", "Nist5"}, 732 | {"keccak", "Keccak"}, 733 | {"quark", "Quark"}, 734 | {"neoscrypt", "NeoScrypt"} 735 | }; 736 | 737 | private string GetAlgoDisplayName(string rawname) 738 | { 739 | if (_algoNames.ContainsKey(rawname)) 740 | return _algoNames[rawname]; 741 | return rawname; 742 | } 743 | 744 | #endregion 745 | } 746 | } 747 | -------------------------------------------------------------------------------- /MinerControl/MiningModeEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MinerControl 7 | { 8 | public enum MiningModeEnum 9 | { 10 | Stopped, 11 | Manual, 12 | Automatic, 13 | Donation 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /MinerControl/PriceEntries/HamsterPoolPriceEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MinerControl.PriceEntries 7 | { 8 | public class HamsterPoolPriceEntry : PriceEntryBase 9 | { 10 | public override decimal Fees 11 | { 12 | get { return Earn * (0.015m + Donation); } 13 | set { base.Fees = value; } 14 | } 15 | 16 | public decimal Donation { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /MinerControl/PriceEntries/LtcRabbitPriceEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MinerControl.PriceEntries 7 | { 8 | public class LtcRabbitPriceEntry : PriceEntryBase 9 | { 10 | public override decimal Fees 11 | { 12 | get { return Earn * 0.02m; } 13 | set { base.Fees = value; } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /MinerControl/PriceEntries/ManualPriceEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MinerControl.PriceEntries 7 | { 8 | public class ManualPriceEntry : PriceEntryBase 9 | { 10 | public decimal FeePercent { get; set; } 11 | 12 | public override decimal Fees 13 | { 14 | get { return Earn * (FeePercent / 100); } 15 | set { base.Fees = value; } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /MinerControl/PriceEntries/NiceHashPriceEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MinerControl.PriceEntries 7 | { 8 | public class NiceHashPriceEntry : PriceEntryBase 9 | { 10 | public override decimal Fees 11 | { 12 | get { return Earn * 0.02m; } 13 | set { base.Fees = value; } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /MinerControl/PriceEntries/PriceEntryBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MinerControl.Services; 4 | using MinerControl.Utility; 5 | 6 | namespace MinerControl.PriceEntries 7 | { 8 | public abstract class PriceEntryBase : PropertyChangedBase 9 | { 10 | public PriceEntryBase() 11 | { 12 | Weight = 1.0m; 13 | DeadTime = DateTime.MinValue; 14 | } 15 | 16 | public MiningEngine MiningEngine { get; set; } 17 | public int Id { get; set; } 18 | public IService ServiceEntry { get; set; } 19 | public string PriceId { get; set; } 20 | public string AlgoName { get; set; } 21 | public string Name { get; set; } 22 | public bool UseWindow { get; set; } 23 | 24 | public decimal Hashrate { get; set; } 25 | public decimal Power { get; set; } 26 | public string Folder { get; set; } 27 | public string Command { get; set; } 28 | public string Arguments { get; set; } 29 | public string DonationFolder { get; set; } 30 | public string DonationCommand { get; set; } 31 | public string DonationArguments { get; set; } 32 | 33 | private decimal _price; 34 | private decimal _fees; 35 | private decimal _weight; 36 | private decimal _balance; 37 | private decimal _acceptSpeed; 38 | private decimal _rejectSpeed; 39 | private TimeSpan _timeMining; 40 | private DateTime _deadTime; 41 | 42 | public decimal Price { get { return _price; } set { SetField(ref _price, value, () => Price, () => Earn, () => Fees, () => NetEarn); } } 43 | public decimal Earn { get { return Price / 1000 * Hashrate / 1000; } } 44 | public decimal PowerCost { get { return Power / 1000 * 24 * MiningEngine.PowerCost / MiningEngine.Exchange; } } 45 | public virtual decimal Fees { get { return _fees; } set { SetField(ref _fees, value, () => Fees, () => NetEarn); } } 46 | public decimal Weight { get { return _weight; } set { SetField(ref _weight, value, () => Weight, () => NetEarn); } } 47 | public decimal NetEarn { get { return ((Earn - Fees) * Weight) - PowerCost; } } 48 | 49 | public decimal Balance { get { return _balance; } set { SetField(ref _balance, value, () => Balance, () => BalancePrint); } } 50 | public decimal AcceptSpeed { get { return _acceptSpeed; } set { SetField(ref _acceptSpeed, value, () => AcceptSpeed, () => AcceptSpeedPrint); } } 51 | public decimal RejectSpeed { get { return _rejectSpeed; } set { SetField(ref _rejectSpeed, value, () => RejectSpeed, () => RejectSpeedPrint); } } 52 | 53 | public TimeSpan TimeMining { get { return _timeMining; } set { SetField(ref _timeMining, value, () => TimeMining, () => TimeMiningPrint); } } 54 | public DateTime DeadTime { get { return _deadTime; } set { SetField(ref _deadTime, value, () => DeadTime, () => StatusPrint); } } 55 | 56 | public bool IsDead { get { return (DeadTime + MiningEngine.DeadTime) > DateTime.Now; } } 57 | public TimeSpan TimeMiningWithCurrent 58 | { 59 | get 60 | { 61 | return MiningEngine.CurrentRunning.HasValue && MiningEngine.CurrentRunning.Value == Id && MiningEngine.StartMining.HasValue 62 | ? (TimeMining + (DateTime.Now - MiningEngine.StartMining.Value)) 63 | : TimeMining; 64 | } 65 | } 66 | 67 | public string ServicePrint { get { return ServiceEntry.ServiceEnum.ToString(); } } 68 | public string BalancePrint { get { return Balance == 0.0m ? string.Empty : Balance.ToString("N8"); } } 69 | public string AcceptSpeedPrint { get { return AcceptSpeed == 0.0m ? string.Empty : AcceptSpeed.ToString("N2"); } } 70 | public string RejectSpeedPrint { get { return RejectSpeed == 0.0m ? string.Empty : RejectSpeed.ToString("N2"); } } 71 | public string TimeMiningPrint { get { return TimeMiningWithCurrent.FormatTime(true); } } 72 | 73 | public string StatusPrint 74 | { 75 | get 76 | { 77 | if (MiningEngine.CurrentRunning.HasValue && MiningEngine.CurrentRunning.Value == Id) 78 | return "Running"; 79 | if (IsDead) 80 | return "Dead"; 81 | if (MiningEngine.NextRun.HasValue && MiningEngine.NextRun.Value == Id) 82 | return "Pending"; 83 | return string.Empty; 84 | } 85 | } 86 | 87 | public void UpdateStatus() 88 | { 89 | OnPropertyChanged(() => StatusPrint); 90 | OnPropertyChanged(() => TimeMiningPrint); 91 | ServiceEntry.UpdateTime(); 92 | } 93 | 94 | public void UpdateExchange() 95 | { 96 | OnPropertyChanged(() => PowerCost); 97 | OnPropertyChanged(() => NetEarn); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /MinerControl/PriceEntries/TradeMyBitPriceEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MinerControl.PriceEntries 7 | { 8 | public class TradeMyBitPriceEntry : PriceEntryBase 9 | { 10 | public override decimal Fees 11 | { 12 | get { return Earn * 0.025m; } 13 | set { base.Fees = value; } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /MinerControl/PriceEntries/WafflePoolPriceEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MinerControl.PriceEntries 7 | { 8 | public class WafflePoolPriceEntry : PriceEntryBase 9 | { 10 | public override decimal Fees 11 | { 12 | get { return Earn * 0.02m; } 13 | set { base.Fees = value; } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /MinerControl/PriceEntries/WePayBtcPriceEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MinerControl.PriceEntries 7 | { 8 | public class WePayBtcPriceEntry : PriceEntryBase 9 | { 10 | public override decimal Fees 11 | { 12 | //get { return Earn * 0.02m; } 13 | get { return 0m; } 14 | set { base.Fees = value; } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /MinerControl/PriceEntries/YaampPriceEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MinerControl.PriceEntries 7 | { 8 | public class YaampPriceEntry : PriceEntryBase 9 | { 10 | public decimal FeePercent { get; set; } 11 | 12 | public override decimal Fees 13 | { 14 | get { return Earn * (FeePercent / 100); } 15 | set { base.Fees = value; } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /MinerControl/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Permissions; 3 | using System.Threading; 4 | using System.Windows.Forms; 5 | using MinerControl.Utility; 6 | 7 | namespace MinerControl 8 | { 9 | static class Program 10 | { 11 | public static bool HasAutoStart { get; set; } 12 | public static bool MinimizeToTray { get; set; } 13 | public static bool MinimizeOnStart { get; set; } 14 | 15 | /// 16 | /// The main entry point for the application. 17 | /// 18 | [STAThread] 19 | [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)] 20 | static void Main(string[] args) 21 | { 22 | foreach (var arg in args) 23 | { 24 | switch (arg) 25 | { 26 | case "-a": 27 | case "--auto-start": 28 | HasAutoStart = true; 29 | break; 30 | case "-t": 31 | case "--minimize-to-tray": 32 | MinimizeToTray = true; 33 | break; 34 | case "-m": 35 | case "--minimize": 36 | MinimizeOnStart = true; 37 | break; 38 | } 39 | } 40 | 41 | Application.EnableVisualStyles(); 42 | Application.SetCompatibleTextRenderingDefault(false); 43 | 44 | Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(LastResortThreadExceptionHandler); 45 | Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); 46 | AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(LastResortDomainExceptionHandler); 47 | 48 | Application.Run(new MainWindow()); 49 | } 50 | 51 | private static void LastResortThreadExceptionHandler(object sender, ThreadExceptionEventArgs e) 52 | { 53 | ErrorLogger.Log(e.Exception); 54 | } 55 | 56 | private static void LastResortDomainExceptionHandler(object sender, UnhandledExceptionEventArgs e) 57 | { 58 | var ex = (Exception)e.ExceptionObject; 59 | ErrorLogger.Log(ex); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /MinerControl/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MinerControl")] 9 | [assembly: AssemblyDescription("Algorithm switching control for mining services")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Miner Control")] 13 | [assembly: AssemblyCopyright("Copyright © 2014 StuffOfInterest aka Delbert Matlock")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("13172645-6ea5-463e-9d8b-a9ac8258f9bf")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.6.1.0")] 36 | [assembly: AssemblyFileVersion("1.6.1.0")] 37 | -------------------------------------------------------------------------------- /MinerControl/ServiceEnum.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MinerControl 7 | { 8 | public enum ServiceEnum 9 | { 10 | NiceHash, 11 | WestHash, 12 | TradeMyBit, 13 | YAAMP, 14 | WafflePool, 15 | LTCRabbit, 16 | WePayBTC, 17 | HamsterPool, 18 | Manual 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /MinerControl/Services/HamsterPoolService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using MinerControl.PriceEntries; 6 | using MinerControl.Utility; 7 | 8 | namespace MinerControl.Services 9 | { 10 | public class HamsterPoolService : ServiceBase 11 | { 12 | // https://hamsterpool.com/index.php?page=api&action=algorithm_btc_per_mh 13 | // {"scrypt":"0.00015374509140941","nscrypt":"0.0012163696966358","sha256":"1.2994452372168E-8","x11":"0.00012615553588975","qubit":"0.00034249512834947","x13":"0.00019299647643157","neoscrypt":"0.0080657456143128"} 14 | 15 | // https://hamsterpool.com/index.php?page=api&action=user_algorithm_balance_btc&api_key=[apikey] 16 | // {"scrypt":0,"nscrypt":0,"x11":8.074500888863218e-8,"x13":0,"sha256":0,"qubit":0,"neoscrypt":0} 17 | 18 | private string _apikey; 19 | private decimal _donation; 20 | 21 | public HamsterPoolService() 22 | { 23 | ServiceEnum = ServiceEnum.HamsterPool; 24 | //DonationAccount = "MinerControl"; //Too hard to configure donation for this one. 25 | //DonationWorker = "1"; 26 | 27 | AlgoTranslations = new Dictionary 28 | { 29 | {"nscrypt", "scryptn"} 30 | }; 31 | } 32 | 33 | public override void Initialize(IDictionary data) 34 | { 35 | ExtractCommon(data); 36 | _apikey = data.GetString("apikey"); 37 | _donation = data["donation"].ExtractDecimal() / 100; 38 | 39 | var items = data["algos"] as object[]; 40 | foreach (var rawitem in items) 41 | { 42 | var item = rawitem as Dictionary; 43 | var entry = CreateEntry(item); 44 | entry.Donation = _donation; 45 | 46 | Add(entry); 47 | } 48 | } 49 | 50 | public override void CheckPrices() 51 | { 52 | ClearStalePrices(); 53 | WebUtil.DownloadJson(string.Format("https://hamsterpool.com/index.php?page=api&action=algorithm_btc_per_mh", _apikey), ProcessPrices); 54 | WebUtil.DownloadJson(string.Format("https://hamsterpool.com/index.php?page=api&action=user_algorithm_balance_btc&api_key={0}", _apikey), ProcessBalances); 55 | } 56 | 57 | private void ProcessPrices(object jsonData) 58 | { 59 | var data = jsonData as Dictionary; 60 | lock (MiningEngine) 61 | { 62 | foreach (var key in data.Keys) 63 | { 64 | var algo = key.ToLower(); 65 | var price = data[key].ExtractDecimal(); 66 | 67 | var entry = GetEntry(algo); 68 | if (entry == null) continue; 69 | 70 | entry.Price = price * 1000; 71 | } 72 | 73 | MiningEngine.PricesUpdated = true; 74 | MiningEngine.HasPrices = true; 75 | 76 | LastUpdated = DateTime.Now; 77 | } 78 | } 79 | 80 | private void ProcessBalances(object jsonData) 81 | { 82 | var data = jsonData as Dictionary; 83 | lock (MiningEngine) 84 | { 85 | foreach (var key in data.Keys) 86 | { 87 | var algo = key.ToLower(); 88 | var balance = data[key].ExtractDecimal(); 89 | 90 | var entry = GetEntry(algo); 91 | if (entry == null) continue; 92 | 93 | entry.Balance = balance; 94 | } 95 | 96 | Balance = PriceEntries.Sum(o => o.Balance); 97 | 98 | MiningEngine.PricesUpdated = true; 99 | MiningEngine.HasPrices = true; 100 | 101 | LastUpdated = DateTime.Now; 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /MinerControl/Services/IService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | 5 | namespace MinerControl.Services 6 | { 7 | public interface IService : INotifyPropertyChanged 8 | { 9 | MiningEngine MiningEngine { get; set; } 10 | ServiceEnum ServiceEnum { get; } 11 | DateTime? LastUpdated { get; } 12 | decimal Balance { get; } 13 | decimal Currency { get; } 14 | 15 | string ServicePrint { get; } 16 | string LastUpdatedPrint { get; } 17 | string BalancePrint { get; } 18 | string CurrencyPrint { get; } 19 | string TimeMiningPrint { get; } 20 | 21 | void Initialize(IDictionary data); 22 | void CheckPrices(); 23 | void UpdateTime(); 24 | void UpdateExchange(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MinerControl/Services/LtcRabbitService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using MinerControl.PriceEntries; 6 | using MinerControl.Utility; 7 | 8 | namespace MinerControl.Services 9 | { 10 | public class LtcRabbitService : ServiceBase 11 | { 12 | // https://www.ltcrabbit.com/index.php?page=api&action=public 13 | // {"hashrate":2394110,"hashrate_scrypt":2394110,"hashrate_x11":3971072,"workers":1765,"ltc_mh_scrypt":"0.0257857","ltc_mh_x11":"0.0328172", 14 | // "profitability": 15 | // {"current":{ 16 | // "scrypt":{"ltc_mh":"0.0257857","btc_mh":"0.0002651","vs_ltc":"93.60"}, 17 | // "x11":{"ltc_mh":"0.0328172","btc_mh":"0.0003374","vs_ltc":"476.49"}}, 18 | // "history":{ 19 | // "scrypt":[{"date":"2014-10-20","ltc_mh":"0.0171152","btc_mh":"0.0001757","vs_ltc":"62.08"},], 20 | // "x11":[{"date":"2014-10-20","ltc_mh":"0.0303198","btc_mh":"0.0003113","vs_ltc":"439.88"},]} 21 | // }} 22 | 23 | // https://www.ltcrabbit.com/index.php?page=api&action=getappdata&appname=general&appversion=1&api_key= 24 | // {"getappdata":{ 25 | // "general":{"message":""}, 26 | // "pool":{"hashrate_scrypt":2271520,"hashrate_x11":3859754,"workers":1783,"ltc_mh_scrypt":"0.0254868","ltc_mh_x11":"0.0323970","hashrate":2271520}, 27 | // "profitability": , 28 | // "ltc_exchange_rates":{"USD":"3.88","EUR":"3.104"}, 29 | // "btc_exchange_rates":{"USD":"379.736","EUR":"301.00000"}, 30 | // "user":{"username":"MinerControl","balance":0,"balance_btc":"0.0000033","hashrate_scrypt":0,"hashrate_x11":0,"invalid_shares_scrypt":0,"invalid_shares_x11":0,"sharerate":0,"invalid_share_rate":0,"hashrate":0}, 31 | // "worker":[], 32 | // "earnings":{"basis":[],"alt":[],"24h_total":0,"24h_basis":0,"24h_alt":0,"24h_affiliate":0,"48h_total":0,"48h_basis":0,"48h_alt":0,"48h_affiliate":0}}} 33 | 34 | private string _apikey; 35 | 36 | public LtcRabbitService() 37 | { 38 | ServiceEnum = ServiceEnum.LTCRabbit; 39 | DonationAccount = "MinerControl"; 40 | DonationWorker = "1"; 41 | } 42 | 43 | public override void Initialize(IDictionary data) 44 | { 45 | ExtractCommon(data); 46 | _apikey = data.GetString("apikey"); 47 | 48 | var items = data["algos"] as object[]; 49 | foreach (var rawitem in items) 50 | { 51 | var item = rawitem as Dictionary; 52 | var entry = CreateEntry(item); 53 | 54 | Add(entry); 55 | } 56 | } 57 | 58 | public override void CheckPrices() 59 | { 60 | ClearStalePrices(); 61 | WebUtil.DownloadJson("https://www.ltcrabbit.com/index.php?page=api&action=public", ProcessPrices); 62 | WebUtil.DownloadJson(string.Format("https://www.ltcrabbit.com/index.php?page=api&action=getappdata&appname=MinerControl&appversion=1&api_key={0}", _apikey), ProcessBalances); 63 | } 64 | 65 | private void ProcessPrices(object jsonData) 66 | { 67 | var data = jsonData as Dictionary; 68 | var profitability = data["profitability"] as Dictionary; 69 | var current = profitability["current"] as Dictionary; 70 | 71 | lock (MiningEngine) 72 | { 73 | foreach (var key in current.Keys) 74 | { 75 | var rawitem = current[key]; 76 | var item = rawitem as Dictionary; 77 | var algo = key.ToLower(); 78 | 79 | var entry = GetEntry(algo); 80 | if (entry == null) continue; 81 | 82 | entry.Price = item["btc_mh"].ExtractDecimal() * 1000; 83 | } 84 | 85 | MiningEngine.PricesUpdated = true; 86 | MiningEngine.HasPrices = true; 87 | 88 | LastUpdated = DateTime.Now; 89 | } 90 | } 91 | 92 | private void ProcessBalances(object jsonData) 93 | { 94 | var data = jsonData as Dictionary; 95 | var getappdata = data["getappdata"] as Dictionary; 96 | var ltc_exchange_rates = getappdata["ltc_exchange_rates"] as Dictionary; 97 | var btc_exchange_rates = getappdata["btc_exchange_rates"] as Dictionary; 98 | var user = getappdata["user"] as Dictionary; 99 | Balance = user["balance_btc"].ExtractDecimal(); 100 | 101 | var entry = GetEntry("x11"); 102 | if (entry != null) 103 | { 104 | var hashrate = user["hashrate_x11"].ExtractDecimal(); 105 | entry.AcceptSpeed = hashrate / 1000; 106 | } 107 | 108 | entry = GetEntry("scrypt"); 109 | if (entry != null) 110 | { 111 | var hashrate = user["hashrate_scrypt"].ExtractDecimal(); 112 | entry.AcceptSpeed = hashrate / 1000; 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /MinerControl/Services/ManualService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using MinerControl.PriceEntries; 6 | 7 | namespace MinerControl.Services 8 | { 9 | public class ManualService : ServiceBase 10 | { 11 | public ManualService() 12 | { 13 | ServiceEnum = ServiceEnum.Manual; 14 | } 15 | 16 | public override void Initialize(IDictionary data) 17 | { 18 | ExtractCommon(data); 19 | 20 | var items = data["algos"] as object[]; 21 | foreach (var rawitem in items) 22 | { 23 | var item = rawitem as Dictionary; 24 | var entry = CreateEntry(item); 25 | 26 | if (item.ContainsKey("price")) 27 | entry.Price = item["price"].ExtractDecimal(); 28 | if (item.ContainsKey("fee")) 29 | entry.FeePercent = item["fee"].ExtractDecimal(); 30 | 31 | Add(entry); 32 | } 33 | } 34 | 35 | public override void CheckPrices() 36 | { 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /MinerControl/Services/NiceHashService.cs: -------------------------------------------------------------------------------- 1 | namespace MinerControl.Services 2 | { 3 | public class NiceHashService : NiceHashServiceBase 4 | { 5 | public NiceHashService() 6 | { 7 | ServiceEnum = ServiceEnum.NiceHash; 8 | } 9 | 10 | protected override string BalanceFormat { get { return "https://www.nicehash.com/api?method=stats.provider&addr={0}"; } } 11 | protected override string CurrentFormat { get { return "https://www.nicehash.com/api?method=stats.global.current"; } } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MinerControl/Services/NiceHashServiceBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using MinerControl.PriceEntries; 5 | using MinerControl.Utility; 6 | 7 | namespace MinerControl.Services 8 | { 9 | public abstract class NiceHashServiceBase : ServiceBase 10 | { 11 | protected abstract string BalanceFormat { get; } 12 | protected abstract string CurrentFormat { get; } 13 | 14 | public NiceHashServiceBase() 15 | { 16 | DonationAccount = "1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y"; 17 | DonationWorker = "1"; 18 | } 19 | 20 | public override void Initialize(IDictionary data) 21 | { 22 | ExtractCommon(data); 23 | 24 | var items = data["algos"] as object[]; 25 | foreach (var rawitem in items) 26 | { 27 | var item = rawitem as Dictionary; 28 | var entry = CreateEntry(item); 29 | if (string.IsNullOrWhiteSpace(entry.PriceId)) 30 | entry.PriceId = GetAgorithmId(entry.AlgoName).ToString(); 31 | 32 | Add(entry); 33 | } 34 | } 35 | 36 | public override void CheckPrices() 37 | { 38 | ClearStalePrices(); 39 | WebUtil.DownloadJson(CurrentFormat, ProcessPrices); 40 | WebUtil.DownloadJson(string.Format(BalanceFormat, _account), ProcessBalances); 41 | } 42 | 43 | private void ProcessPrices(object jsonData) 44 | { 45 | var data = jsonData as Dictionary; 46 | var result = data["result"] as Dictionary; 47 | var stats = result["stats"] as object[]; 48 | 49 | lock (MiningEngine) 50 | { 51 | foreach (var stat in stats) 52 | { 53 | var item = stat as Dictionary; 54 | var algo = item["algo"].ToString(); 55 | var entry = GetEntry(algo); 56 | if (entry == null) continue; 57 | 58 | entry.Price = item["price"].ExtractDecimal(); 59 | switch (entry.AlgoName) 60 | { 61 | case "sha256": 62 | entry.Price = item["price"].ExtractDecimal() / 1000; // SHA256 listed in TH/s 63 | break; 64 | default: 65 | entry.Price = item["price"].ExtractDecimal(); // All others in GH/s 66 | break; 67 | } 68 | } 69 | 70 | MiningEngine.PricesUpdated = true; 71 | MiningEngine.HasPrices = true; 72 | 73 | LastUpdated = DateTime.Now; 74 | } 75 | } 76 | 77 | private void ProcessBalances(object jsonData) 78 | { 79 | var totalBalance = 0m; 80 | var data = jsonData as Dictionary; 81 | var result = data["result"] as Dictionary; 82 | var stats = result["stats"] as object[]; 83 | foreach (var stat in stats) 84 | { 85 | var item = stat as Dictionary; 86 | totalBalance += item["balance"].ExtractDecimal(); 87 | var algo = item["algo"].ToString(); 88 | var entry = GetEntry(algo); 89 | if (entry == null) continue; 90 | 91 | entry.Balance = item["balance"].ExtractDecimal(); 92 | switch (entry.AlgoName) 93 | { 94 | case "sha256": 95 | entry.AcceptSpeed = item["accepted_speed"].ExtractDecimal(); 96 | entry.RejectSpeed = item["rejected_speed"].ExtractDecimal(); 97 | break; 98 | default: 99 | entry.AcceptSpeed = item["accepted_speed"].ExtractDecimal() * 1000; 100 | entry.RejectSpeed = item["rejected_speed"].ExtractDecimal() * 1000; 101 | break; 102 | } 103 | } 104 | 105 | lock (MiningEngine) 106 | { 107 | Balance = totalBalance; 108 | } 109 | } 110 | 111 | private IDictionary _algoTranslation = new Dictionary 112 | { 113 | {"x11", 3}, 114 | {"x13", 4}, 115 | {"scrypt", 0}, 116 | {"scryptn", 2}, 117 | {"keccak", 5}, 118 | {"sha256", 1}, 119 | {"x15", 6}, 120 | {"nist5", 7}, 121 | {"neoscrypt", 8} 122 | }; 123 | 124 | private int GetAgorithmId(string algorithmName) 125 | { 126 | return _algoTranslation[algorithmName]; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /MinerControl/Services/ServiceBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using MinerControl.PriceEntries; 5 | using MinerControl.Utility; 6 | 7 | namespace MinerControl.Services 8 | { 9 | public abstract class ServiceBase : PropertyChangedBase, IService 10 | where TEntry : PriceEntryBase, new() 11 | { 12 | private ServiceEnum _serviceEnum; 13 | private DateTime? _lastUpdated; 14 | private decimal _balance; 15 | 16 | public MiningEngine MiningEngine { get; set; } 17 | public ServiceEnum ServiceEnum { get { return _serviceEnum; } protected set { SetField(ref _serviceEnum, value, () => ServiceEnum, () => ServicePrint); } } 18 | public DateTime? LastUpdated { get { return _lastUpdated; } protected set { SetField(ref _lastUpdated, value, () => LastUpdated, () => LastUpdatedPrint); } } 19 | public decimal Balance { get { return _balance; } protected set { SetField(ref _balance, value, () => Balance, () => BalancePrint, () => CurrencyPrint); } } 20 | public decimal Currency { get { return Balance * MiningEngine.Exchange; } } 21 | 22 | public virtual string ServicePrint { get { return ServiceEnum.ToString(); } } 23 | public string LastUpdatedPrint { get { return LastUpdated == null ? string.Empty : LastUpdated.Value.ToString("HH:mm:ss"); } } 24 | public string BalancePrint { get { return Balance == 0.0m ? string.Empty : Balance.ToString("N8"); } } 25 | public string CurrencyPrint { get { return Currency == 0.0m ? string.Empty : Currency.ToString("N4"); } } 26 | public string TimeMiningPrint 27 | { 28 | get 29 | { 30 | var seconds = PriceEntries.Sum(o => o.TimeMiningWithCurrent.TotalSeconds); 31 | return TimeSpan.FromSeconds(seconds).FormatTime(true); 32 | } 33 | } 34 | 35 | public ServiceBase() 36 | { 37 | DonationAccount = string.Empty; 38 | DonationWorker = string.Empty; 39 | } 40 | 41 | public void UpdateTime() 42 | { 43 | OnPropertyChanged(() => TimeMiningPrint); 44 | } 45 | 46 | public void UpdateExchange() 47 | { 48 | OnPropertyChanged(() => CurrencyPrint); 49 | } 50 | 51 | protected IList PriceEntries { get { return MiningEngine.PriceEntries.Where(o => o.ServiceEntry.ServiceEnum == ServiceEnum).Select(o => (TEntry)o).ToList(); } } 52 | 53 | protected string _account; 54 | protected string _worker; 55 | protected string _param1; 56 | protected string _param2; 57 | protected string _param3; 58 | protected decimal _weight = 1.0m; 59 | protected string DonationAccount { get; set;} 60 | protected string DonationWorker { get; set; } 61 | protected IDictionary AlgoTranslations { get; set; } 62 | 63 | public abstract void Initialize(IDictionary data); 64 | public abstract void CheckPrices(); 65 | 66 | protected void ExtractCommon(IDictionary data) 67 | { 68 | _account = data.GetString("account") ?? string.Empty; 69 | _worker = data.GetString("worker") ?? string.Empty; 70 | if (data.ContainsKey("weight")) 71 | _weight = data["weight"].ExtractDecimal(); 72 | _param1 = data.GetString("sparam1") ?? data.GetString("param1") ?? string.Empty; 73 | _param2 = data.GetString("sparam2") ?? data.GetString("param2") ?? string.Empty; 74 | _param3 = data.GetString("sparam3") ?? data.GetString("param3") ?? string.Empty; 75 | } 76 | 77 | protected string ProcessedSubstitutions(string raw, AlgorithmEntry algo) 78 | { 79 | if (string.IsNullOrWhiteSpace(raw)) return null; 80 | raw = raw 81 | .Replace("_ACCOUNT_", _account) 82 | .Replace("_WORKER_", _worker); 83 | return ProcessCommon(raw, algo); 84 | } 85 | 86 | protected string ProcessedDonationSubstitutions(string raw, AlgorithmEntry algo) 87 | { 88 | if (string.IsNullOrWhiteSpace(raw)) return null; 89 | raw = raw 90 | .Replace("_ACCOUNT_", DonationAccount) 91 | .Replace("_WORKER_", DonationWorker); 92 | return ProcessCommon(raw, algo); 93 | } 94 | 95 | private string ProcessCommon(string raw, AlgorithmEntry algo) 96 | { 97 | return raw 98 | .Replace("_PARAM1_", _param1) 99 | .Replace("_PARAM2_", _param2) 100 | .Replace("_PARAM3_", _param3) 101 | .Replace("_SPARAM1_", _param1) 102 | .Replace("_SPARAM2_", _param2) 103 | .Replace("_SPARAM3_", _param3) 104 | .Replace("_APARAM1_", algo.Param1) 105 | .Replace("_APARAM2_", algo.Param2) 106 | .Replace("_APARAM3_", algo.Param3); 107 | } 108 | 109 | protected TEntry CreateEntry(Dictionary item) 110 | { 111 | var entry = new TEntry(); 112 | entry.MiningEngine = MiningEngine; 113 | entry.ServiceEntry = this; 114 | 115 | entry.AlgoName = item.GetString("algo"); 116 | var algo = MiningEngine.AlgorithmEntries.Single(o => o.Name == entry.AlgoName); 117 | entry.Name = algo.Display; 118 | entry.PriceId = item.GetString("priceid"); 119 | entry.Hashrate = algo.Hashrate; 120 | entry.Power = algo.Power; 121 | entry.Weight = _weight; 122 | entry.Folder = ProcessedSubstitutions(item.GetString("folder"), algo) ?? string.Empty; 123 | entry.Command = ProcessedSubstitutions(item.GetString("command"), algo); 124 | entry.Arguments = ProcessedSubstitutions(item.GetString("arguments"), algo) ?? string.Empty; 125 | if(item.ContainsKey("usewindow")) 126 | entry.UseWindow = bool.Parse(item["usewindow"].ToString()); 127 | if (!string.IsNullOrWhiteSpace(DonationAccount)) 128 | { 129 | entry.DonationFolder = ProcessedDonationSubstitutions(item.GetString("folder"), algo) ?? string.Empty; 130 | entry.DonationCommand = ProcessedDonationSubstitutions(item.GetString("command"), algo); 131 | entry.DonationArguments = ProcessedDonationSubstitutions(item.GetString("arguments"), algo) ?? string.Empty; 132 | } 133 | 134 | return entry; 135 | } 136 | 137 | protected void Add(TEntry entry) 138 | { 139 | MiningEngine.PriceEntries.Add(entry); 140 | } 141 | 142 | private string GetAlgoName(string name) 143 | { 144 | if (AlgoTranslations == null || !AlgoTranslations.ContainsKey(name)) return name; 145 | return AlgoTranslations[name]; 146 | } 147 | 148 | protected TEntry GetEntry(string algo) 149 | { 150 | return PriceEntries.FirstOrDefault(o => (o.PriceId != null && o.PriceId == algo) || (o.PriceId == null && o.AlgoName == GetAlgoName(algo))); 151 | } 152 | 153 | protected void ClearStalePrices() 154 | { 155 | if (!LastUpdated.HasValue || LastUpdated.Value.AddMinutes(30) > DateTime.Now) return; 156 | 157 | foreach (var entry in PriceEntries) 158 | entry.Price = 0; 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /MinerControl/Services/TradeMyBitService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using MinerControl.PriceEntries; 5 | using MinerControl.Utility; 6 | 7 | namespace MinerControl.Services 8 | { 9 | public class TradeMyBitService : ServiceBase 10 | { 11 | // https://pool.trademybit.com/api/bestalgo?key=[key] 12 | // [{"algo":"x11","score":"0.00136681","actual":"0.000248511"},{"algo":"nscrypt","score":"0.00111675","actual":"0.00237607"},{"algo":"scrypt","score":"0.000443541","actual":"0.000443541"},{"algo":"nist5","score":"0.0000663135","actual":"0.000004019"},{"algo":"x13","score":"0.0000659915","actual":"0.0000178355"}] 13 | 14 | // https://pool.trademybit.com/api/balance?key=[key] 15 | // {"autoexchange":{"est_total":"0.00130840","unexchanged":"0.00000000","exchanged":"0.00130840","alltime":"0.00000000"}} 16 | 17 | private string _apikey; 18 | 19 | public TradeMyBitService() 20 | { 21 | ServiceEnum = ServiceEnum.TradeMyBit; 22 | DonationAccount = "MinerControl"; 23 | DonationWorker = "1"; 24 | 25 | AlgoTranslations = new Dictionary 26 | { 27 | {"nscrypt", "scryptn"} 28 | }; 29 | } 30 | 31 | public override void Initialize(IDictionary data) 32 | { 33 | ExtractCommon(data); 34 | _apikey = data.GetString("apikey"); 35 | 36 | var items = data["algos"] as object[]; 37 | foreach (var rawitem in items) 38 | { 39 | var item = rawitem as Dictionary; 40 | var entry = CreateEntry(item); 41 | Add(entry); 42 | } 43 | } 44 | 45 | public override void CheckPrices() 46 | { 47 | ClearStalePrices(); 48 | WebUtil.DownloadJson(string.Format("https://pool.trademybit.com/api/bestalgo?key={0}", _apikey), ProcessPrices); 49 | WebUtil.DownloadJson(string.Format("https://pool.trademybit.com/api/balance?key={0}", _apikey), ProcessBalances); 50 | } 51 | 52 | private void ProcessPrices(object jsonData) 53 | { 54 | var data = jsonData as object[]; 55 | lock (MiningEngine) 56 | { 57 | foreach (var rawitem in data) 58 | { 59 | var item = rawitem as Dictionary; 60 | var algo = item.GetString("algo"); 61 | var entry = GetEntry(algo); 62 | if (entry == null) continue; 63 | 64 | entry.Price = item["actual"].ExtractDecimal() * 1000; 65 | } 66 | 67 | MiningEngine.PricesUpdated = true; 68 | MiningEngine.HasPrices = true; 69 | 70 | LastUpdated = DateTime.Now; 71 | } 72 | } 73 | 74 | private void ProcessBalances(object jsonData) 75 | { 76 | var data = jsonData as Dictionary; 77 | var balances = data["autoexchange"] as Dictionary; 78 | 79 | lock (MiningEngine) 80 | { 81 | Balance = balances["est_total"].ExtractDecimal(); 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /MinerControl/Services/WafflePoolService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using MinerControl.PriceEntries; 5 | using MinerControl.Utility; 6 | 7 | namespace MinerControl.Services 8 | { 9 | public class WafflePoolService : ServiceBase 10 | { 11 | // http://wafflepool.com/api/stats 12 | // {"scrypt":{ 13 | // "mining":"hidden", 14 | // "hashrate":22229349799, 15 | // "last_hour":{"aricoin":1.69,"feathercoin":20.61,"litecoin":31.07,"litecoindark":2.32,"megacoin":6.69,"monacoin":3.19,"nautiluscoin":5.7,"noblecoin":4.56,"potcoin":3.29,"tagcoin":3.44,"usde":3.79,"viacoin":13.66}, 16 | // "balances":{"sent":15957.24412827,"exchanged":5.5655921,"confirmed":2.3906955,"unconfirmed":1.49996499}, 17 | // "earnings":[ 18 | // {"date":"2014-10-06","earned":3.65661792,"hashrate":22652460350,"permhs":0.00036595,"vsltc":116.02}, 19 | // {"date":"2014-10-05","earned":7.3391845,"hashrate":22683413258,"permhs":0.00032355,"vsltc":100.25}, 20 | // {"date":"2014-10-04","earned":8.25865688,"hashrate":20843006208,"permhs":0.00039623,"vsltc":123.13}, 21 | // {"date":"2014-10-03","earned":6.38886382,"hashrate":16476333450,"permhs":0.00038776,"vsltc":121.41}, 22 | // {"date":"2014-10-02","earned":6.67626292,"hashrate":22812053995,"permhs":0.00029266,"vsltc":89.81}, 23 | // {"date":"2014-10-01","earned":7.99719669,"hashrate":11478782808,"permhs":0.00069669,"vsltc":212.72}, 24 | // {"date":"2014-09-30","earned":5.90335632,"hashrate":15462069191,"permhs":0.0003818,"vsltc":117.91}]}, 25 | // "nscrypt":{ 26 | // "mining":"murraycoin","hashrate":159737729,"last_hour":{"murraycoin":2.61,"spaincoin":3.52,"vertcoin":93.87},"balances":{"sent":156.60008804,"exchanged":0.15470803,"confirmed":0,"unconfirmed":0.04396441},"earnings":[{"date":"2014-10-06","earned":0.08699461,"hashrate":158068034,"permhs":0.0012477,"vsltc":185.92},{"date":"2014-10-05","earned":0.23894878,"hashrate":171170636,"permhs":0.00139597,"vsltc":203.28},{"date":"2014-10-04","earned":0.30048782,"hashrate":198166747,"permhs":0.00151634,"vsltc":221.46},{"date":"2014-10-03","earned":0.34351864,"hashrate":204521631,"permhs":0.00167962,"vsltc":247.16},{"date":"2014-10-02","earned":0.46233947,"hashrate":240756592,"permhs":0.00192036,"vsltc":276.99},{"date":"2014-10-01","earned":0.38214958,"hashrate":248980112,"permhs":0.00153486,"vsltc":220.26},{"date":"2014-09-30","earned":0.42550327,"hashrate":254810422,"permhs":0.00166988,"vsltc":242.38}]}, 27 | // "x11":{ 28 | // "mining":"startcoinv2","hashrate":18529509790,"last_hour":{"cannabiscoin":5.93,"cryptcoin":0.21,"darkcoin":8.47,"fractalcoin":0.13,"glyphcoin":0.19,"startcoinv2":81.72,"urocoin":3.19,"virtualcoin":0.15},"balances":{"sent":1058.00456076,"exchanged":5.22549376,"confirmed":0.02933664,"unconfirmed":0.08405081},"earnings":[{"date":"2014-10-06","earned":4.3002266,"hashrate":17715027860,"permhs":0.00055032,"vsltc":697.88},{"date":"2014-10-05","earned":8.16684463,"hashrate":16683302480,"permhs":0.00048952,"vsltc":606.68},{"date":"2014-10-04","earned":6.19759441,"hashrate":15926108606,"permhs":0.00038915,"vsltc":483.71},{"date":"2014-10-03","earned":5.38855032,"hashrate":15966248607,"permhs":0.0003375,"vsltc":422.67},{"date":"2014-10-02","earned":3.92883941,"hashrate":14658970986,"permhs":0.00026802,"vsltc":329},{"date":"2014-10-01","earned":2.82580133,"hashrate":12714976659,"permhs":0.00022224,"vsltc":271.43},{"date":"2014-09-30","earned":3.05441985,"hashrate":14278611823,"permhs":0.00021392,"vsltc":264.25}]}, 29 | // "x13":{ 30 | // "mining":null,"hashrate":10995107,"last_hour":[],"balances":{"sent":138.95795525,"exchanged":0.04238868,"confirmed":0,"unconfirmed":0},"earnings":[{"date":"2014-10-06","earned":0,"hashrate":0,"permhs":0,"vsltc":0},{"date":"2014-10-05","earned":0,"hashrate":0,"permhs":0,"vsltc":0},{"date":"2014-10-04","earned":0,"hashrate":0,"permhs":0,"vsltc":0},{"date":"2014-10-03","earned":0,"hashrate":0,"permhs":0,"vsltc":0},{"date":"2014-10-02","earned":0,"hashrate":0,"permhs":0,"vsltc":0},{"date":"2014-10-01","earned":0,"hashrate":0,"permhs":0,"vsltc":0},{"date":"2014-09-30","earned":0,"hashrate":0,"permhs":0,"vsltc":0}]}} 31 | 32 | // http://wafflepool.com/api/miner?address=1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y 33 | // {"scrypt":{"hashrate":0,"hashrate_str":"0.00 kH\/s","stalerate":0,"stalerate_str":"0.00 kH\/s","workers":[],"balances":{"sent":0,"confirmed":0,"unconverted":0}}, 34 | // "nscrypt":{"hashrate":0,"hashrate_str":"0.00 kH\/s","stalerate":0,"stalerate_str":"0.00 kH\/s","workers":[],"balances":{"sent":0,"confirmed":0,"unconverted":0}}, 35 | // "x11":{ 36 | // "hashrate":1215199,"hashrate_str":"1.22 MH\/s","stalerate":0,"stalerate_str":"0.00 kH\/s", 37 | // "workers":{"1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y":{"hashrate":1215199,"stalerate":0,"str":"1.22 MH\/s","last_seen":1412633470}}, 38 | // "balances":{"sent":0,"confirmed":0,"unconverted":0}}, 39 | // "x13":{"hashrate":0,"hashrate_str":"0.00 kH\/s","stalerate":0,"stalerate_str":"0.00 kH\/s","workers":[],"balances":{"sent":0,"confirmed":0,"unconverted":0}}} 40 | 41 | public WafflePoolService() 42 | { 43 | ServiceEnum = ServiceEnum.WafflePool; 44 | DonationAccount = "1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y"; 45 | DonationWorker = "1"; 46 | 47 | AlgoTranslations = new Dictionary 48 | { 49 | {"nscrypt", "scryptn"} 50 | }; 51 | } 52 | 53 | public override void Initialize(IDictionary data) 54 | { 55 | ExtractCommon(data); 56 | 57 | var items = data["algos"] as object[]; 58 | foreach (var rawitem in items) 59 | { 60 | var item = rawitem as Dictionary; 61 | var entry = CreateEntry(item); 62 | 63 | Add(entry); 64 | } 65 | } 66 | 67 | public override void CheckPrices() 68 | { 69 | ClearStalePrices(); 70 | WebUtil.DownloadJson("http://wafflepool.com/api/stats", ProcessPrices); 71 | WebUtil.DownloadJson(string.Format("http://wafflepool.com/api/miner?address={0}", _account), ProcessBalances); 72 | } 73 | 74 | private void ProcessPrices(object jsonData) 75 | { 76 | var data = jsonData as Dictionary; 77 | lock (MiningEngine) 78 | { 79 | foreach (var key in data.Keys) 80 | { 81 | var rawitem = data[key]; 82 | var item = rawitem as Dictionary; 83 | 84 | var entry = GetEntry(key.ToLower()); 85 | if (entry == null) continue; 86 | 87 | var earnings = item["earnings"] as object[]; 88 | var earning = earnings[0] as Dictionary; 89 | 90 | entry.Price = earning["permhs"].ExtractDecimal() * 1000; 91 | } 92 | 93 | MiningEngine.PricesUpdated = true; 94 | MiningEngine.HasPrices = true; 95 | 96 | LastUpdated = DateTime.Now; 97 | } 98 | } 99 | 100 | private void ProcessBalances(object jsonData) 101 | { 102 | var data = jsonData as Dictionary; 103 | 104 | lock (MiningEngine) 105 | { 106 | foreach (var entry in PriceEntries) 107 | { 108 | entry.Balance = 0; 109 | entry.AcceptSpeed = 0; 110 | entry.RejectSpeed = 0; 111 | } 112 | 113 | foreach (var key in data.Keys) 114 | { 115 | var rawitem = data[key]; 116 | var item = rawitem as Dictionary; 117 | 118 | var entry = GetEntry(key.ToLower()); 119 | if (entry == null) continue; 120 | 121 | entry.AcceptSpeed = item["hashrate"].ExtractDecimal() / 1000000; 122 | entry.RejectSpeed = item["stalerate"].ExtractDecimal() / 1000000; 123 | 124 | var balances = item["balances"] as Dictionary; 125 | entry.Balance = balances["confirmed"].ExtractDecimal() + balances["unconverted"].ExtractDecimal(); 126 | } 127 | 128 | Balance = PriceEntries.Select(o => o.Balance).Sum(); 129 | 130 | MiningEngine.PricesUpdated = true; 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /MinerControl/Services/WePayBtcService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using MinerControl.PriceEntries; 6 | using MinerControl.Utility; 7 | 8 | namespace MinerControl.Services 9 | { 10 | public class WePayBtcService : ServiceBase 11 | { 12 | // http://wepaybtc.com/payouts.json 13 | 14 | //{ 15 | // "reference": 0.001, 16 | // "x11": 0.0002066, 17 | // "x13": 0.0002985, 18 | // "x15": 0.0003717, 19 | // "nist5": 0.00006863 20 | //} 21 | 22 | public WePayBtcService() 23 | { 24 | ServiceEnum = ServiceEnum.WePayBTC; 25 | DonationAccount = "1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y"; 26 | } 27 | 28 | public override void Initialize(IDictionary data) 29 | { 30 | ExtractCommon(data); 31 | 32 | var items = data["algos"] as object[]; 33 | foreach (var rawitem in items) 34 | { 35 | var item = rawitem as Dictionary; 36 | var entry = CreateEntry(item); 37 | 38 | Add(entry); 39 | } 40 | } 41 | 42 | public override void CheckPrices() 43 | { 44 | ClearStalePrices(); 45 | WebUtil.DownloadJson("http://wepaybtc.com/payouts.json", ProcessPrices); 46 | } 47 | 48 | private void ProcessPrices(object jsonData) 49 | { 50 | var data = jsonData as Dictionary; 51 | 52 | lock (MiningEngine) 53 | { 54 | foreach (var key in data.Keys) 55 | { 56 | var rawitem = data[key]; 57 | var item = rawitem as Dictionary; 58 | var algo = key.ToLower(); 59 | 60 | var entry = GetEntry(algo); 61 | if (entry == null) continue; 62 | 63 | entry.Price = data[key].ExtractDecimal() * 1000; 64 | } 65 | 66 | MiningEngine.PricesUpdated = true; 67 | MiningEngine.HasPrices = true; 68 | 69 | LastUpdated = DateTime.Now; 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /MinerControl/Services/WestHashService.cs: -------------------------------------------------------------------------------- 1 | namespace MinerControl.Services 2 | { 3 | public class WestHashService : NiceHashServiceBase 4 | { 5 | public WestHashService() 6 | { 7 | ServiceEnum = ServiceEnum.WestHash; 8 | } 9 | 10 | protected override string BalanceFormat { get { return "https://www.westhash.com/api?method=stats.provider&addr={0}"; } } 11 | protected override string CurrentFormat { get { return "https://www.westhash.com/api?method=stats.global.current"; } } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /MinerControl/Services/YaampService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using MinerControl.PriceEntries; 5 | using MinerControl.Utility; 6 | 7 | namespace MinerControl.Services 8 | { 9 | public class YaampService : ServiceBase 10 | { 11 | // http://yaamp.com/api/status 12 | // { 13 | // "scrypt": {"name": "scrypt", "port": 3433, "coins": 21, "fees": 0.5, "hashrate": 1585708947, "estimate_current": 0.00017441, "estimate_last24h": 0.00018214, "actual_last24h": 0.00019935}, 14 | // "scryptn": {"name": "scryptn", "port": 4333, "coins": 3, "fees": 3.2, "hashrate": 88775354, "estimate_current": 0.00042183, "estimate_last24h": 0.00045701, "actual_last24h": 0.00039583}, 15 | // "neoscrypt": {"name": "neoscrypt", "port": 4233, "coins": 4, "fees": 2.8, "hashrate": 359254951, "estimate_current": 0.00217158, "estimate_last24h": 0.00352892, "actual_last24h": 0.00320471}, 16 | // "quark": {"name": "quark", "port": 4033, "coins": 3, "fees": 0.5, "hashrate": 85473584, "estimate_current": 0.00000078, "estimate_last24h": 0.00000087, "actual_last24h": 0.00020362}, 17 | // "lyra2": {"name": "lyra2", "port": 4433, "coins": 1, "fees": 0.6, "hashrate": 16864909, "estimate_current": 0.00062095, "estimate_last24h": 0.00063257, "actual_last24h": 0.00090811}, 18 | // "x11": {"name": "x11", "port": 3533, "coins": 10, "fees": 1.3, "hashrate": 7793602368, "estimate_current": 0.00019716, "estimate_last24h": 0.00015537, "actual_last24h": 0.00016760}, 19 | // "x13": {"name": "x13", "port": 3633, "coins": 2, "fees": 3.7, "hashrate": 2500273917, "estimate_current": 0.00019537, "estimate_last24h": 0.00016803, "actual_last24h": 0.00017710}, 20 | // "x14": {"name": "x14", "port": 3933, "coins": 1, "fees": 3.5, "hashrate": 54660672, "estimate_current": 0.00032863, "estimate_last24h": 0.00025539, "actual_last24h": 0.00025089}, 21 | // "x15": {"name": "x15", "port": 3733, "coins": 3, "fees": 5.4, "hashrate": 5870269795, "estimate_current": 0.00126368, "estimate_last24h": 0.00025544, "actual_last24h": 0.00082305} 22 | // } 23 | 24 | private int _priceMode; 25 | 26 | public YaampService() 27 | { 28 | ServiceEnum = ServiceEnum.YAAMP; 29 | DonationAccount = "1PMj3nrVq5CH4TXdJSnHHLPdvcXinjG72y"; 30 | } 31 | 32 | public override void Initialize(IDictionary data) 33 | { 34 | ExtractCommon(data); 35 | 36 | if (data.ContainsKey("pricemode")) 37 | _priceMode = int.Parse(data["pricemode"].ToString()); 38 | 39 | var items = data["algos"] as object[]; 40 | foreach (var rawitem in items) 41 | { 42 | var item = rawitem as Dictionary; 43 | var entry = CreateEntry(item); 44 | 45 | Add(entry); 46 | } 47 | } 48 | 49 | public override void CheckPrices() 50 | { 51 | ClearStalePrices(); 52 | WebUtil.DownloadJson("http://yaamp.com/api/status", ProcessPrices); 53 | WebUtil.DownloadJson(string.Format("http://yaamp.com/api/wallet?address={0}", _account), ProcessBalances); 54 | } 55 | 56 | private void ProcessPrices(object jsonData) 57 | { 58 | var data = jsonData as Dictionary; 59 | 60 | lock (MiningEngine) 61 | { 62 | foreach (var key in data.Keys) 63 | { 64 | var rawitem = data[key]; 65 | var item = rawitem as Dictionary; 66 | var algo = key.ToLower(); 67 | 68 | var entry = GetEntry(algo); 69 | if (entry == null) continue; 70 | 71 | switch (_priceMode) 72 | { 73 | case 1: 74 | entry.Price = item["estimate_last24h"].ExtractDecimal() * 1000; 75 | break; 76 | case 2: 77 | entry.Price = item["actual_last24h"].ExtractDecimal() * 1000; 78 | break; 79 | default: 80 | entry.Price = item["estimate_current"].ExtractDecimal() * 1000; 81 | break; 82 | } 83 | entry.FeePercent = item["fees"].ExtractDecimal(); 84 | } 85 | 86 | MiningEngine.PricesUpdated = true; 87 | MiningEngine.HasPrices = true; 88 | 89 | LastUpdated = DateTime.Now; 90 | } 91 | } 92 | 93 | private void ProcessBalances(object jsonData) 94 | { 95 | var data = jsonData as Dictionary; 96 | 97 | lock (MiningEngine) 98 | { 99 | Balance = data["unpaid"].ExtractDecimal(); 100 | 101 | foreach (var entry in PriceEntries) 102 | entry.AcceptSpeed = 0; 103 | 104 | if (!data.ContainsKey("miners")) return; 105 | var miners = data["miners"] as Dictionary; 106 | foreach (var key in miners.Keys) 107 | { 108 | var entry = GetEntry(key.ToLower()); 109 | if (entry == null) continue; 110 | var item = miners[key] as Dictionary; 111 | entry.AcceptSpeed = item["hashrate"].ExtractDecimal() / 1000000; 112 | } 113 | 114 | MiningEngine.PricesUpdated = true; 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /MinerControl/Utility/ErrorLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace MinerControl.Utility 6 | { 7 | public static class ErrorLogger 8 | { 9 | const string logfile = "error.log"; 10 | 11 | public static bool LogExceptions { get; set; } 12 | 13 | public static void Log(Exception ex) 14 | { 15 | var sb = new StringBuilder(); 16 | sb.AppendLine(); 17 | sb.AppendLine(DateTime.Now.ToString()); 18 | sb.AppendLine("----------------------------------------------"); 19 | sb.AppendLine(string.Format("Type: {0}", ex.GetType().Name)); 20 | sb.AppendLine(string.Format("Message: {0}", ex.Message)); 21 | sb.AppendLine(string.Format("Stack trace: {0}", ex.StackTrace)); 22 | 23 | using (var w = File.Exists(logfile) ? File.AppendText(logfile) : File.CreateText(logfile)) 24 | { 25 | w.Write(sb.ToString()); 26 | } 27 | 28 | if (ex.InnerException != null) 29 | Log(ex.InnerException); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /MinerControl/Utility/Multicast/MulticastDataReceivedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | 7 | namespace MinerControl.Utility.Multicast 8 | { 9 | public class MulticastDataReceivedEventArgs : EventArgs 10 | { 11 | private byte[] _data; 12 | private IPEndPoint _remote; 13 | 14 | public MulticastDataReceivedEventArgs(IPEndPoint remote, byte[] data) 15 | { 16 | _remote = remote; 17 | _data = data; 18 | } 19 | 20 | public IPEndPoint RemoteEndPoint { get { return _remote; } } 21 | public byte[] Data { get { return _data; } } 22 | public string StringData { get { return Encoding.Unicode.GetString(_data); } } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /MinerControl/Utility/Multicast/MulticastReceiver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | using System.Threading; 8 | 9 | namespace MinerControl.Utility.Multicast 10 | { 11 | public delegate void MulticastDataReceivedEventHandler(object sender, MulticastDataReceivedEventArgs e); 12 | 13 | public class MulticastReceiver : IDisposable 14 | { 15 | private Thread _listener; 16 | private IPEndPoint _endPoint; 17 | 18 | public event MulticastDataReceivedEventHandler DataReceived; 19 | 20 | public MulticastReceiver(IPEndPoint endPoint) 21 | { 22 | _endPoint = endPoint; 23 | } 24 | 25 | public void Start() 26 | { 27 | _listener = new Thread(new ThreadStart(BackgroundListener)) 28 | { 29 | IsBackground = true 30 | }; 31 | _listener.Start(); 32 | } 33 | 34 | public void Stop() 35 | { 36 | lock (this) 37 | { 38 | _listener.Abort(); 39 | _listener.Join(500); 40 | } 41 | } 42 | 43 | private void BackgroundListener() 44 | { 45 | var bindingEndpoint = new IPEndPoint(IPAddress.Any, _endPoint.Port); 46 | using (var client = new UdpClient()) 47 | { 48 | client.ExclusiveAddressUse = false; 49 | client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 50 | client.Client.Bind(bindingEndpoint); 51 | client.JoinMulticastGroup(_endPoint.Address); 52 | 53 | var keepRunning = true; 54 | while (keepRunning) 55 | { 56 | try 57 | { 58 | IPEndPoint remote = new IPEndPoint(IPAddress.Any, _endPoint.Port); 59 | var buffer = client.Receive(ref remote); 60 | lock (this) 61 | { 62 | DataReceived(this, new MulticastDataReceivedEventArgs(remote, buffer)); 63 | } 64 | } 65 | catch (ThreadAbortException) 66 | { 67 | keepRunning = false; 68 | Thread.ResetAbort(); 69 | } 70 | } 71 | 72 | client.DropMulticastGroup(_endPoint.Address); 73 | } 74 | } 75 | 76 | private bool _disposed; 77 | 78 | public void Dispose() 79 | { 80 | if (_disposed) return; 81 | if (_listener.IsAlive) 82 | { 83 | Stop(); 84 | } 85 | _disposed = true; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /MinerControl/Utility/Multicast/MulticastSender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | 8 | namespace MinerControl.Utility.Multicast 9 | { 10 | public class MulticastSender : IDisposable 11 | { 12 | private IPEndPoint _endPoint; 13 | private int _timeToLive; 14 | private UdpClient _udpClient; 15 | 16 | public MulticastSender(IPEndPoint endPoint, int timeToLive) 17 | { 18 | _endPoint = endPoint; 19 | _timeToLive = timeToLive; 20 | } 21 | 22 | public void Start() 23 | { 24 | var bindingEndpoint = new IPEndPoint(IPAddress.Any, _endPoint.Port); 25 | 26 | _udpClient = new UdpClient(); 27 | _udpClient.ExclusiveAddressUse = false; 28 | _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 29 | _udpClient.Client.Bind(bindingEndpoint); 30 | _udpClient.JoinMulticastGroup(_endPoint.Address, _timeToLive); 31 | } 32 | 33 | public void Stop() 34 | { 35 | _udpClient.DropMulticastGroup(_endPoint.Address); 36 | _udpClient.Close(); 37 | _udpClient = null; 38 | } 39 | 40 | public void Send(byte[] data) 41 | { 42 | _udpClient.Send(data, data.Length, _endPoint); 43 | } 44 | 45 | public void Send(string data) 46 | { 47 | Send(Encoding.Unicode.GetBytes(data)); 48 | } 49 | 50 | private bool _disposed; 51 | 52 | public void Dispose() 53 | { 54 | if (_disposed) return; 55 | if (_udpClient != null) 56 | { 57 | Stop(); 58 | } 59 | _disposed = true; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /MinerControl/Utility/ProcessUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Management; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | 9 | namespace MinerControl.Utility 10 | { 11 | public static class ProcessUtil 12 | { 13 | /// 14 | /// Kill a process, and all of its children, grandchildren, etc. 15 | /// 16 | /// Process ID. 17 | public static void KillProcessAndChildren(int pid) 18 | { 19 | ManagementObjectSearcher searcher = new ManagementObjectSearcher 20 | ("Select * From Win32_Process Where ParentProcessID=" + pid); 21 | ManagementObjectCollection moc = searcher.Get(); 22 | foreach (ManagementObject mo in moc) 23 | { 24 | KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"])); 25 | } 26 | try 27 | { 28 | Process proc = Process.GetProcessById(pid); 29 | proc.Kill(); 30 | } 31 | catch (ArgumentException) 32 | { 33 | // Process already exited. 34 | } 35 | catch (Exception ex) 36 | { 37 | ErrorLogger.Log(ex); 38 | } 39 | } 40 | 41 | public static void KillProcess(Process process) 42 | { 43 | try 44 | { 45 | if (process == null || process.HasExited == true) 46 | return; 47 | 48 | process.Kill(); 49 | } 50 | catch (ArgumentException) 51 | { 52 | // Process already exited. 53 | } 54 | catch (Exception ex) 55 | { 56 | ErrorLogger.Log(ex); 57 | } 58 | } 59 | 60 | //http://stackoverflow.com/questions/2647820/toggle-process-startinfo-windowstyle-processwindowstyle-hidden-at-runtime 61 | [DllImport("user32.dll")] 62 | static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); 63 | 64 | public static void HideWindow(Process process) 65 | { 66 | if (process == null || process.HasExited) return; 67 | ShowWindow(process.MainWindowHandle, 0); // Hidden 68 | } 69 | 70 | public static void MinimizeWindow(Process process) 71 | { 72 | if (process == null || process.HasExited) return; 73 | ShowWindow(process.MainWindowHandle, 2); // Minimized 74 | } 75 | 76 | [DllImport("user32.dll")] 77 | static extern int SetWindowText(IntPtr hWnd, string text); 78 | 79 | public static void SetWindowTitle(Process process, string title) 80 | { 81 | SetWindowText(process.MainWindowHandle, title); 82 | } 83 | 84 | /// 85 | /// Safely check if process exists and has not exited. 86 | /// 87 | public static bool IsRunning(this Process process) 88 | { 89 | try 90 | { 91 | if (process == null || process.HasExited) return false; 92 | } 93 | catch (InvalidOperationException) // Happens if the process did not launch but object still exists 94 | { 95 | return false; 96 | } 97 | 98 | return true; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /MinerControl/Utility/PropertyChangedBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Text; 7 | 8 | namespace MinerControl.Utility 9 | { 10 | // Adapted from examples here: 11 | // http://stackoverflow.com/questions/1315621/implementing-inotifypropertychanged-does-a-better-way-exist 12 | public abstract class PropertyChangedBase : INotifyPropertyChanged 13 | { 14 | public event PropertyChangedEventHandler PropertyChanged; 15 | 16 | protected virtual void OnPropertyChanged(string propertyName) 17 | { 18 | PropertyChangedEventHandler handler = PropertyChanged; 19 | if (handler != null) 20 | handler(this, new PropertyChangedEventArgs(propertyName)); 21 | } 22 | 23 | protected virtual void OnPropertyChanged(Expression> selectorExpression) 24 | { 25 | if (selectorExpression == null) 26 | throw new ArgumentNullException("selectorExpression"); 27 | var me = selectorExpression.Body as MemberExpression; 28 | 29 | // Nullable properties can be nested inside of a convert function 30 | if (me == null) 31 | { 32 | var ue = selectorExpression.Body as UnaryExpression; 33 | if (ue != null) 34 | { 35 | me = ue.Operand as MemberExpression; 36 | } 37 | } 38 | 39 | if (me == null) 40 | throw new ArgumentException("The body must be a member expression"); 41 | 42 | OnPropertyChanged(me.Member.Name); 43 | } 44 | 45 | protected void SetField(ref T field, T value, Expression> selectorExpression, params Expression>[] additonal) 46 | { 47 | if (EqualityComparer.Default.Equals(field, value)) return; 48 | field = value; 49 | OnPropertyChanged(selectorExpression); 50 | foreach (var item in additonal) 51 | OnPropertyChanged(item); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /MinerControl/Utility/SlidingBuffer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace MinerControl.Utility 5 | { 6 | // Taken from http://stackoverflow.com/questions/6392516 7 | 8 | public class SlidingBuffer : IEnumerable 9 | { 10 | private readonly Queue _queue; 11 | private readonly int _maxCount; 12 | 13 | public SlidingBuffer(int maxCount) 14 | { 15 | _maxCount = maxCount; 16 | _queue = new Queue(maxCount); 17 | } 18 | 19 | public void Add(T item) 20 | { 21 | if (_queue.Count == _maxCount) 22 | _queue.Dequeue(); 23 | _queue.Enqueue(item); 24 | } 25 | 26 | public IEnumerator GetEnumerator() 27 | { 28 | return _queue.GetEnumerator(); 29 | } 30 | 31 | IEnumerator IEnumerable.GetEnumerator() 32 | { 33 | return GetEnumerator(); 34 | } 35 | 36 | public int Count 37 | { 38 | get { return _queue.Count; } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /MinerControl/Utility/SortableBindingList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace MinerControl.Utility 8 | { 9 | // http://www.martinwilley.com/net/code/forms/sortablebindinglist.html 10 | public class SortableBindingList : BindingList where T : class 11 | { 12 | private bool _isSorted; 13 | private ListSortDirection _sortDirection = ListSortDirection.Ascending; 14 | private PropertyDescriptor _sortProperty; 15 | 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public SortableBindingList() 20 | { 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// An of items to be contained in the . 27 | public SortableBindingList(IList list) 28 | : base(list) 29 | { 30 | } 31 | 32 | /// 33 | /// Gets a value indicating whether the list supports sorting. 34 | /// 35 | protected override bool SupportsSortingCore 36 | { 37 | get { return true; } 38 | } 39 | 40 | /// 41 | /// Gets a value indicating whether the list is sorted. 42 | /// 43 | protected override bool IsSortedCore 44 | { 45 | get { return _isSorted; } 46 | } 47 | 48 | /// 49 | /// Gets the direction the list is sorted. 50 | /// 51 | protected override ListSortDirection SortDirectionCore 52 | { 53 | get { return _sortDirection; } 54 | } 55 | 56 | /// 57 | /// Gets the property descriptor that is used for sorting the list if sorting is implemented in a derived class; otherwise, returns null 58 | /// 59 | protected override PropertyDescriptor SortPropertyCore 60 | { 61 | get { return _sortProperty; } 62 | } 63 | 64 | /// 65 | /// Removes any sort applied with ApplySortCore if sorting is implemented 66 | /// 67 | protected override void RemoveSortCore() 68 | { 69 | _sortDirection = ListSortDirection.Ascending; 70 | _sortProperty = null; 71 | _isSorted = false; //thanks Luca 72 | } 73 | 74 | /// 75 | /// Sorts the items if overridden in a derived class 76 | /// 77 | /// 78 | /// 79 | protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction) 80 | { 81 | _sortProperty = prop; 82 | _sortDirection = direction; 83 | 84 | List list = Items as List; 85 | if (list == null) return; 86 | 87 | list.Sort(Compare); 88 | 89 | _isSorted = true; 90 | //fire an event that the list has been changed. 91 | OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); 92 | } 93 | 94 | private int Compare(T lhs, T rhs) 95 | { 96 | var result = OnComparison(lhs, rhs); 97 | //invert if descending 98 | if (_sortDirection == ListSortDirection.Descending) 99 | result = -result; 100 | return result; 101 | } 102 | 103 | private int OnComparison(T lhs, T rhs) 104 | { 105 | object lhsValue = lhs == null ? null : _sortProperty.GetValue(lhs); 106 | object rhsValue = rhs == null ? null : _sortProperty.GetValue(rhs); 107 | if (lhsValue == null) 108 | { 109 | return (rhsValue == null) ? 0 : -1; //nulls are equal 110 | } 111 | if (rhsValue == null) 112 | { 113 | return 1; //first has value, second doesn't 114 | } 115 | if (lhsValue is IComparable) 116 | { 117 | return ((IComparable)lhsValue).CompareTo(rhsValue); 118 | } 119 | if (lhsValue.Equals(rhsValue)) 120 | { 121 | return 0; //both are the same 122 | } 123 | //not comparable, compare ToString 124 | return lhsValue.ToString().CompareTo(rhsValue.ToString()); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /MinerControl/Utility/WebUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Web.Script.Serialization; 7 | 8 | namespace MinerControl.Utility 9 | { 10 | public static class WebUtil 11 | { 12 | public static void DownloadJson(string url, Action jsonProcessor) 13 | { 14 | try 15 | { 16 | using (var client = new WebClient()) 17 | { 18 | var uri = new Uri(url); 19 | client.Encoding = Encoding.UTF8; 20 | client.DownloadStringCompleted += DownloadJsonComplete; 21 | client.DownloadStringAsync(uri, jsonProcessor); 22 | } 23 | } 24 | catch (Exception ex) 25 | { 26 | ErrorLogger.Log(ex); 27 | } 28 | } 29 | 30 | private static void DownloadJsonComplete(object sender, DownloadStringCompletedEventArgs e) 31 | { 32 | try 33 | { 34 | var pageString = e.Result; 35 | if (pageString == null) return; 36 | var jsonProcessor = e.UserState as Action; 37 | var serializer = new JavaScriptSerializer(); 38 | var data = serializer.DeserializeObject(pageString); 39 | 40 | jsonProcessor(data); 41 | } 42 | catch (Exception ex) 43 | { 44 | ErrorLogger.Log(ex); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /MinerControl/bitcoin.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StuffOfInterest/MinerControl/0ed1d0b1654195dabe939196a8350dc2e3aef867/MinerControl/bitcoin.ico -------------------------------------------------------------------------------- /MinerControl/test-run.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | echo Launching from batch file... 3 | testminer %1 %2 %3 %4 %5 %6 %7 %8 %9 4 | pause 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Miner Control 2 | ============= 3 | 4 | CyrptoCurrency auto-switching miner controller 5 | 6 | Details are on the Bitcoin Talk thread here: https://bitcointalk.org/index.php?topic=769239 7 | -------------------------------------------------------------------------------- /TestMiner/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /TestMiner/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace TestMiner 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | var x = 1; 11 | Console.WriteLine("I'm pretending to mine."); 12 | if (args.Length > 0) 13 | { 14 | Console.WriteLine(string.Format("Arguments: {0}", string.Join(" ", args))); 15 | } 16 | 17 | while(true) 18 | { 19 | Thread.Sleep(1000); 20 | Console.WriteLine(string.Format("Found block {0}!", x)); 21 | x++; 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /TestMiner/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TestMiner")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TestMiner")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("639983c3-981e-4cad-94e7-955c2b3c107e")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TestMiner/TestMiner.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {63721D21-B76A-40AE-9A37-0C6F392CEE63} 8 | Exe 9 | Properties 10 | TestMiner 11 | TestMiner 12 | v4.0 13 | 512 14 | 15 | 16 | 17 | x86 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 59 | --------------------------------------------------------------------------------