Giter Club home page Giter Club logo

mikaylafischler / cc-mek-scada Goto Github PK

View Code? Open in Web Editor NEW
272.0 14.0 8.0 2.58 MB

Configurable ComputerCraft SCADA system for multi-reactor control of Mekanism fission reactors with a GUI, automatic safety features, waste processing control, and more! Please be sure to take a look at the Wiki tab, this project has lots of docs!

Home Page: https://youtube.com/playlist?list=PLPTRLQjcSlD2s6HsPe3COlfaULQZogWKK

License: MIT License

Lua 99.73% Python 0.27%
cc-tweaked mekanism minecraft scada comptuercraft

cc-mek-scada's Introduction

cc-mek-scada

Configurable ComputerCraft SCADA system for multi-reactor control of Mekanism fission reactors with a GUI, automatic safety features, waste processing control, and more!

GitHub GitHub release (latest by date including pre-releases) GitHub Workflow Status (with branch) GitHub Workflow Status (with branch)

Discord

Released Component Versions

Installer

Bootloader Comms Comms Graphics Lockbox

Reactor PLC RTU Supervisor Coordinator Pocket

Requirements

Mod Requirements:

  • CC: Tweaked
  • Mekanism v10.1+

Mod Recommendations:

  • Advanced Peripherals (adds the capability to detect environmental radiation levels)
  • Immersive Engineering (provides bundled redstone, though any mod containing bundled redstone will do)

v10.1+ is required due to the complete support of CC:Tweaked added in Mekanism v10.1

Installation

You can install this on a ComputerCraft computer using either:

  • wget https://raw.githubusercontent.com/MikaylaFischler/cc-mek-scada/main/ccmsi.lua
  • pastebin get sqUN6VUb ccmsi.lua

Contributing

Please reach out to me via Discord or email (or GitHub in some way) if you are thinking of making any contributions at this time. I started this project as a challenge for myself and have been enjoying having something I can work on in my own way.

Once this is out of beta I will be more open to contributions, but for now I am hoping to keep them to a minimum as the remaining challenges are ones I am looking forward to solving.

Supervisory control and data acquisition (SCADA) is a control system architecture comprising computers, networked data communications and graphical user interfaces for high-level supervision of machines and processes. It also covers sensors and other devices, such as programmable logic controllers, which interface with process plant or machinery.

This project implements concepts of a SCADA system in ComputerCraft (because why not? ..okay don't answer that). I recommend reviewing that linked wikipedia page on SCADA if you want to understand the concepts used here.

Architecture

SCADA and industrial automation terminology is used throughout the project, such as:

  • Supervisory Computer: Gathers data and controls the process
  • Coordinating Computer: Used as the HMI component, user requests high-level processing operations
  • RTU: Remote Terminal Unit
  • PLC: Programmable Logic Controller

ComputerCraft Architecture

Coordinator Server

There can only be one of these. This server acts as a hybrid of levels 3 & 4 in the SCADA diagram above. In addition to viewing status and controlling processes with advanced monitors, it can host access for one or more Pocket computers.

Supervisory Computers

There should be one of these per facility system. Currently, that means only one. In the future, multiple supervisors would provide the capability of coordinating between multiple facilities (like a fission facility, fusion facility, etc).

RTUs

RTUs are effectively basic connections between a device and the SCADA system with no internal logic providing the system with I/O capabilities. A single Advanced Computer can represent multiple RTUs as instead I am modeling an RTU as the wired modems connected to that computer rather than the computer itself. Each RTU is referenced separately with an identifier in the modbus communications (see Communications section), so a single computer can distribute instructions to multiple devices. This should save on having a pile of computers everywhere (but if you want to have that, no one's stopping you).

The RTU control code is relatively unique, as instead of having instructions be decoded simply, due to using modbus, I implemented a generalized RTU interface. To fulfill this, each type of I/O operation is linked to a function rather than implementing the logic itself. For example, to connect an input register to a turbine getFlowRate() call, the function reference itself is passed to the connect_input_reg() function. A call to read_input_reg() on that register address will call the turbine.getFlowRate() function and return the result.

PLCs

PLCs are advanced devices that allow for both reporting and control to/from the SCADA system in addition to programed behaviors independent of the SCADA system. Currently there is only one type of PLC, and that is the reactor PLC. This is responsible for reporting on and controlling the reactor as a part of the SCADA system, and independently regulating the safety of the reactor. It checks the status for multiple hazard scenarios and shuts down the reactor if any condition is met.

There can and should only be one of these per reactor. A single Advanced Computer will act as the PLC, with either a direct connection (physical contact) or a wired modem connection to the reactor logic port.

Communications

A vaguely-modbus modbus communication protocol is used for communication with RTUs. Useful terminology for you to know:

  • Discrete Inputs: Single Bit Read-Only (digital inputs)
  • Coils: Single Bit Read/Write (digital I/O)
  • Input Registers: Multi-Byte Read-Only (analog inputs)
  • Holding Registers: Multi-Byte Read/Write (analog I/O)

Security

HMAC message authentication is available as a configuration option to prevent replay attacks and generally prevent control or false data reporting within a system's network. This is done utilizing the lua-lockbox project.

The other, simpler security feature is to enforce a maximum authorized transmission range, which is also a configurable feature on each device.

cc-mek-scada's People

Contributors

mikaylafischler avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cc-mek-scada's Issues

HMAC for Message Authentication

Utilizing the Lua Lockbox project: https://github.com/somesocks/lua-lockbox
Another option is the pure Lua SHA project: https://github.com/Egor-Skriptunoff/pure_lua_SHA

Network Changes

  • Network interface object implementation
  • Network integration changes for all devices
  • MAC init/config file checks
  • Versioned lockbox component
  • Send serialized string instead of table as payload, serialize function doesn't guarantee order

Notes on HMAC-MD5 with lua-lockbox

Considerations:
https://crypto.stackexchange.com/questions/39305/is-it-safe-to-send-hmac-and-plaintext-in-the-same-message
https://crypto.stackexchange.com/questions/26510/why-is-hmac-sha1-still-considered-secure
https://en.wikipedia.org/wiki/HMAC#Security

Excerpt from https://datatracker.ietf.org/doc/html/rfc6151:

The attacks on HMAC-MD5 do not seem to indicate a practical
vulnerability when used as a message authentication code.
Considering that the distinguishing-H attack is different from a
distinguishing-R attack, which distinguishes an HMAC from a random
function, the practical impact on HMAC usage as a pseudorandom
function (PRF) such as in a key derivation function is not well
understood.

Therefore, it may not be urgent to remove HMAC-MD5 from the existing
protocols. However, since MD5 must not be used for digital
signatures, for a new protocol design, a ciphersuite with HMAC-MD5
should not be included. Options include HMAC-SHA256 [HMAC]
[HMAC-SHA256] and [AES-CMAC] when AES is more readily available than
a hash function.

Example Test Message (unit statuses to coordinator):

local testmsg = "{1,0,42,3,{5,{{},{boilers={},turbines={},rad_mon={},},{TurbineOnline={false,},AutoControl=false,TurbineTrip={false,},HeatingRateLow={false,},HighStartupRate=false,BoilRateMismatch=false,ManualReactorSCRAM=false,FuelInputRateLow=false,PLCHeartbeat=false,MaxWaterReturnFeed=false,RCSFault=false,PLCOnline=false,RadiationMonitor=1,TurbineOverSpeed={false,},CoolantFeedMismatch=false,BoilerOnline={false,},ReactorTempHigh=false,SteamDumpOpen={1,},RCSFlowLow=false,RadiationWarning=false,WasteLineOcclusion=false,SteamFeedMismatch=false,ReactorSCRAM=false,EmergencyCoolant=1,CoolantLevelLow=false,ReactorHighDeltaT=false,AutoReactorSCRAM=false,WaterLevelLow={},RCPTrip=false,GeneratorTrip={false,},},{1,1,1,1,1,1,1,1,1,1,1,1,},{\"REACTOR OFF-LINE\",\"awaiting connection...\",1,false,true,},},{{},{boilers={},turbines={},rad_mon={},},{TurbineOnline={false,},AutoControl=false,TurbineTrip={false,},HeatingRateLow={false,},HighStartupRate=false,BoilRateMismatch=false,ManualReactorSCRAM=false,FuelInputRateLow=false,PLCHeartbeat=false,MaxWaterReturnFeed=false,RCSFault=false,PLCOnline=false,RadiationMonitor=1,TurbineOverSpeed={false,},CoolantFeedMismatch=false,BoilerOnline={false,},ReactorTempHigh=false,SteamDumpOpen={1,},RCSFlowLow=false,RadiationWarning=false,WasteLineOcclusion=false,SteamFeedMismatch=false,ReactorSCRAM=false,EmergencyCoolant=1,CoolantLevelLow=false,ReactorHighDeltaT=false,AutoReactorSCRAM=false,WaterLevelLow={},RCPTrip=false,GeneratorTrip={false,},},{1,1,1,1,1,1,1,1,1,1,1,1,},{\"REACTOR OFF-LINE\",\"awaiting connection...\",1,false,true,},},{{},{boilers={},turbines={},rad_mon={},},{TurbineOnline={false,},AutoControl=false,TurbineTrip={false,},HeatingRateLow={false,},HighStartupRate=false,BoilRateMismatch=false,ManualReactorSCRAM=false,FuelInputRateLow=false,PLCHeartbeat=false,MaxWaterReturnFeed=false,RCSFault=false,PLCOnline=false,RadiationMonitor=1,TurbineOverSpeed={false,},CoolantFeedMismatch=false,BoilerOnline={false,},ReactorTempHigh=false,SteamDumpOpen={1,},RCSFlowLow=false,RadiationWarning=false,WasteLineOcclusion=false,SteamFeedMismatch=false,ReactorSCRAM=false,EmergencyCoolant=1,CoolantLevelLow=false,ReactorHighDeltaT=false,AutoReactorSCRAM=false,WaterLevelLow={},RCPTrip=false,GeneratorTrip={false,},},{1,1,1,1,1,1,1,1,1,1,1,1,},{\"REACTOR OFF-LINE\",\"awaiting connection...\",1,false,true,},},{{},{boilers={},turbines={},rad_mon={},},{TurbineOnline={false,},AutoControl=false,TurbineTrip={false,},HeatingRateLow={false,},HighStartupRate=false,BoilRateMismatch=false,ManualReactorSCRAM=false,FuelInputRateLow=false,PLCHeartbeat=false,MaxWaterReturnFeed=false,RCSFault=false,PLCOnline=false,RadiationMonitor=1,TurbineOverSpeed={false,},CoolantFeedMismatch=false,BoilerOnline={false,},ReactorTempHigh=false,SteamDumpOpen={1,},RCSFlowLow=false,RadiationWarning=false,WasteLineOcclusion=false,SteamFeedMismatch=false,ReactorSCRAM=false,EmergencyCoolant=1,CoolantLevelLow=false,ReactorHighDeltaT=false,AutoReactorSCRAM=false,WaterLevelLow={},RCPTrip=false,GeneratorTrip={false,},},{1,1,1,1,1,1,1,1,1,1,1,1,},{\"REACTOR OFF-LINE\",\"awaiting connection...\",1,false,true,},},},}"

Testing done on CraftOS-PC on Macbook Air M2:
10000 iterations of HMAC-MD5: 9358ms (0.9358ms average per hmac)
10000 iterations of HMAC-SHA1: 9979ms (0.9979ms average per hmac)
10000 iterations of HMAC-SHA224: 11628ms (1.1628ms average per hmac)
10000 iterations of HMAC-SHA256 11882ms (1.1882ms average per hmac)

Testing done on moderately loaded Minecraft 1.16 Server:
1000 iterations of HMAC-MD5: 2340ms (2.34ms average per hmac)
1000 iterations of HMAC-SHA1: 3351ms (3.351ms average per hmac)
1000 iterations of HMAC-SHA224: 5294ms (5.294ms average per hmac)
1000 iterations of HMAC-SHA256 3493ms (3.493ms average per hmac)

Testing done on minimally loaded Minecraft 1.19 Server:
1000 iterations of HMAC-MD5: 1654ms (1.65ms average per hmac)
1000 iterations of HMAC-SHA1: 1840ms (1.84ms average per hmac)
1000 iterations of HMAC-SHA224: 2077ms (2.08ms average per hmac)
1000 iterations of HMAC-SHA256 2449ms (2.45ms average per hmac)

MD5 or SHA1 it is then. 1.5-3ms for the very large coordinator packets that get sent twice a second should be acceptable.

Pure Lua SHA

Testing done on minimally loaded Minecraft 1.19 Server:
1000 iterations of HMAC-MD5: 2895ms (2.90ms average per hmac)
1000 iterations of HMAC-SHA1: 6992ms (6.99ms average per hmac)

Surprisingly, even though this advertised higher speeds, it yielded worse performance when it came to HMACs at least. One of the runs caused a too long without yielding error as well. lua-lockbox it is then.

Coordinator Process Groupings

Reactors should be able to be placed into auto or manual mode. Auto ones will be available for process control.

  • Groups can be made to divide up load across multiple auto mode reactors
  • Can also be set to use the fewest number of reactors (with priority order)

Examples:
Group A: two reactors (max 10mB/t each)
Group B: two reactors (max 10mB/t each)
Group C: four reactors (max 10mB/t each)

  • Set group A to 20 mB/t -> each sets to 10mB/t
  • Set group B to balanced reserve mode for group A -> each set to 0mB/t
  • Set group C to 20 mB/t -> each sets to 5mB/t

Later setting group A to 30 mB/t now will set group A to 10mB/t each then group B to 5mB/t each.

Primary (implied): used first
Reserve (w/ priority): used if needed

Balanced: split among the group
Sequential: fully utilize the first reactor before starting the next

  • Unit Display Interface
  • Coordinator Back-end
  • Supervisor Back-end

Process Induction Matrix Charge Self Limiting

The system should monitor induction matrix charge level and slow/stop the reactors as it nears high charge percentage.

  • SCRAM at limit
  • Hold until threshold before re-enabling to prevent rapid enable/disable
  • High charge state to wait in, returning from it would re-init process controllers so this is preferable

Process Waste Control

Automatic (coordinated) waste processing control.

Support optionally switching to plutonium production during the night when polonium processing is not possible, which would also apply to switching off of antimatter.

  • Auto waste control interface
  • Coordinated waste control (all units assigned to the same target)
  • Day/night balancing option requiring the below task
  • Solar neutron activator integration
  • SPS integration (basic status)

Auto control will always control all units assigned to the same output. If the user wants some to do one thing and others to do another, that's what manual assignment is for.

Process Target Energy Storage

Coordinator should Coordinator should provide functionality for the user to request a specific quantity of energy to maintain in induction matrices. It will then attempt to satisfy all input requirements to maintain that charge level.

  • Calculate moving averages of charge
  • Adjust burn rate to reach target with closed loop control
  • Test control loop logic
  • Tune gains

PLC Crash On Disconnect

PLC crashes when ISS checks occur on a newly disconnected reactor that was not enabled while connected.

Reduce Status Packet Time Cost

Takes almost a full second to fetch all the data for a status packet. Remove some data or conditionally cache certain fields?

PLC Ramp Burn Rate to Setpoint

This could probably safely be done at around 5mB/sec, maybe faster.

Without this feature, a setpoint of 250mB/t from a setpoint of 50mB/t (for example) would flash boil the water too quick for the boiler/turbine to provide returned coolant before the reactor starts to overheat and take damage. This is known from experience.

Boiler: Repeated Disconnect/Reconnect Events

This bug has occurred on an RTU alpha-v0.2.3. Re-forming the boiler temporarily fixed it, but the problem quickly returned. Re-wiring the modems had no affect.

[Fri Apr 22 10:24:59 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:24:59 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:24:59 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:24:59 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:24:59 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:24:59 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:24:59 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:24:59 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:24:59 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:24:59 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:24:59 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:24:59 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:24:59 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:24:59 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:00 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:00 2022] [DBG] PPM: mount(boiler_3) -> found a boiler
[Fri Apr 22 10:25:01 2022] [WRN] PPM: lost device boiler mounted to boiler_3
[Fri Apr 22 10:25:01 2022] [DBG] PPM: mount(boiler_3) -> found a boiler

RTU Unit Threads

RTU thread per unit with busy flag, respond with MODBUS TCP error code device busy if in progress.

Mekanism API Calls use pullEvent

This makes sense considering they need to await the actual server thread, but this breaks my event queues until I change them to use the parallel API, so that's what needs to be done.

Differentiate Wireless vs Wired Modems

Only wireless modems should be used for comms, wired modems are for external peripherals. Only wireless modems should be used to initialize comms objects, so there needs to be a function to specifically get those.

Comms Connection Close

Add close connection type to SCADA_MGMT packet type. Use this to close sessions cleanly without having to wait for the watchdog.

PLC Re-connection

PLCs should continuously retry supervisor connection after timeout rather than exit.

SPS RTU

Would be able to check that SPS rate is matching necessary process rate. Also can notice if we stop receiving polonium, implying we are no longer processing polonium (night time?).

Process Target Energy Generation

Coordinator should provide functionality for the user to simply request a specific target generation rate. It will then attempt to satisfy all input requirements to attain as close to the generation rate as possible.

  • Calculate initial expected required burn rates based on turbine blade math
  • Adjust to target with closed loop control
  • Test control loop logic
  • Tune gains
  • Make blade mismatch check for generation rate mode only

ISS Terminate nil Handling

Terminate exits are caught by PPM, but ISS does not check for nil before comparisons and then crashes anyway.

Process Fuel Self Limiting

The system should monitor fuel percentages and ramp down the setpoint until the fuel input rate is net positive.

Net Flow Monitoring

Flows of coolant/water/steam should correlate. They may deviate during ramp up, but there should be maximum limits at which point the coordinator should attempt to compensate by slowing the reactor. This could detect coolant line failures early.

Re-do Comms Layer

Need to change how data is stored, and it should only be sent as basic arrays.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.