🔨 Add base projet

This commit is contained in:
Sebastien Clement 2021-01-27 23:36:51 +01:00
parent 162a7c6d5b
commit 457ffc366f
33 changed files with 13221 additions and 3 deletions

94
.gitignore vendored
View File

@ -1,3 +1,5 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
logs
*.log
@ -41,8 +43,8 @@ build/Release
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
@ -74,9 +76,11 @@ typings/
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
@ -84,7 +88,7 @@ dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
@ -102,3 +106,87 @@ dist
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

5
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

12
.idea/cnc-speed-calculator.iml generated Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/jsLibraryMappings.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/cnc-speed-calculator.iml" filepath="$PROJECT_DIR$/.idea/cnc-speed-calculator.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

25
.idea/watcherTasks.xml generated Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions">
<TaskOptions isEnabled="true">
<option name="arguments" value="$FileName$:$FileNameWithoutExtension$.css" />
<option name="checkSyntaxErrors" value="true" />
<option name="description" />
<option name="exitCodeBehavior" value="ERROR" />
<option name="fileExtension" value="scss" />
<option name="immediateSync" value="true" />
<option name="name" value="SCSS" />
<option name="output" value="$FileNameWithoutExtension$.css:$FileNameWithoutExtension$.css.map" />
<option name="outputFilters">
<array />
</option>
<option name="outputFromStdout" value="false" />
<option name="program" value="sass" />
<option name="runOnExternalChanges" value="true" />
<option name="scopeName" value="Unnamed" />
<option name="trackOnlyRoot" value="true" />
<option name="workingDir" value="$FileDir$" />
<envs />
</TaskOptions>
</component>
</project>

75
app.js Normal file
View File

@ -0,0 +1,75 @@
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('./config/winston');
const sassMiddleware = require('node-sass-middleware');
const expressWinston = require('express-winston');
const i18n = require('i18n');
const indexRouter = require('./routes/index');
const app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
i18n.configure({
// setup some locales - other locales default to en silently
locales: ['en', 'fr'],
// where to store json files - defaults to './locales'
directory: __dirname + '/locales'
});
// Logger
app.use(expressWinston.logger({
winstonInstance: logger,
meta: true, // optional: control whether you want to log the meta data about the request (default to true)
expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true
colorize: true, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red).
level: function (req, res) {
if (res.statusCode < 500)
return "debug";
return "warn";
}
}));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(sassMiddleware({
src: path.join(__dirname, 'public'),
dest: path.join(__dirname, 'public'),
indentedSyntax: true, // true = .sass and false = .scss
sourceMap: true
}));
app.use(express.static(path.join(__dirname, 'public')));
app.use(i18n.init)
app.use('/', indexRouter);
// Boootstrap JS Files
app.use('/js/bootstrap.min.js', express.static(path.join(__dirname, '/node_modules/bootstrap/dist/js/bootstrap.min.js')))
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;

110
bin/www Executable file
View File

@ -0,0 +1,110 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
const app = require('../app');
const debug = require('debug')('cnc-speed-calculator:server');
const http = require('http');
const sequelize = require("../sequelize");
const init_db = require("../sequelize/init-db")
const logger = require("../config/winston")
/**
* Get port from environment and store in Express.
*/
const port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
let port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
let bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
let addr = server.address();
let bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
logger.info('Listening on ' + bind);
}
async function init() {
console.log(`Checking database connection...`);
try {
await sequelize.authenticate();
logger.info('Database connection OK!');
await init_db.check_database()
} catch (error) {
logger.error('Unable to connect to the database:');
logger.error(error);
process.exit(1);
}
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
}
init();

27
config/winston.js Normal file
View File

@ -0,0 +1,27 @@
const winston = require("winston");
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp({
format: "YYYY-MM-DD HH:mm:ss",
}),
winston.format.errors({ stack: true }),
// winston.format.splat(),
winston.format.colorize(),
winston.format.align(),
winston.format.printf(({ level, message, timestamp }) => {
return `[${timestamp}] [${level}]: ${message}`;
})
),
transports: [
//
// - Write to all logs with level `info` and below to `quick-start-combined.log`.
// - Write all logs error (and below) to `quick-start-error.log`.
//
new winston.transports.Console({ handleExceptions: true }),
// new winston.transports.File({filename: '/data/NCB.log', handleExceptions: true})
],
});
module.exports = logger;

BIN
db.sqlite Normal file

Binary file not shown.

1
locales/en.json Normal file
View File

@ -0,0 +1 @@
{}

19
locales/fr.json Normal file
View File

@ -0,0 +1,19 @@
{
"Material": "Matière",
"Step down factor": "Facteur de profondeur de passe",
"Tool Diameter": "Tool Diameter",
"Number of tooth": "Number of tooth",
"Settings": "Settings",
"Calculated Values": "Calculated Values",
"Spindle Speed": "Spindle Speed",
"Feed Rate": "Feed Rate",
"Step Down": "Step Down",
"Feed Rate (mm/sec": "Feed Rate (mm/sec",
"Feed Rate (mm/sec)": "Feed Rate (mm/sec)",
"Feed Rate (mm/min)": "Feed Rate (mm/min)",
"Constants": "Constants",
"Cutting Speed": "Cutting Speed",
"Feed By Tooth": "Feed By Tooth",
"Step Down factor": "Step Down factor",
"Spindle Max Speed": "Spindle Max Speed"
}

2414
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "cnc-speed-calculator",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"bootstrap": "^5.0.0-beta1",
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"ejs": "~2.6.1",
"express": "~4.16.1",
"express-winston": "^4.0.5",
"http-errors": "~1.6.3",
"i18n": "^0.13.2",
"morgan": "~1.9.1",
"node-sass-middleware": "0.11.0",
"sequelize": "^6.5.0",
"sqlite3": "^5.0.1",
"winston": "^3.3.3"
}
}

37
public/css/bootstrap_imports.scss vendored Normal file
View File

@ -0,0 +1,37 @@
// Layout & components
@import "../../node_modules/bootstrap/scss/root";
@import "../../node_modules/bootstrap/scss/reboot";
@import "../../node_modules/bootstrap/scss/type";
@import "../../node_modules/bootstrap/scss/images";
@import "../../node_modules/bootstrap/scss/containers";
@import "../../node_modules/bootstrap/scss/grid";
//@import "tables";
@import "../../node_modules/bootstrap/scss/forms";
@import "../../node_modules/bootstrap/scss/buttons";
@import "../../node_modules/bootstrap/scss/transitions";
@import "../../node_modules/bootstrap/scss/dropdown";
//@import "button-group";
//@import "nav";
@import "../../node_modules/bootstrap/scss/navbar";
@import "../../node_modules/bootstrap/scss/card";
//@import "accordion";
//@import "breadcrumb";
//@import "pagination";
@import "../../node_modules/bootstrap/scss/badge";
@import "../../node_modules/bootstrap/scss/alert";
@import "../../node_modules/bootstrap/scss/progress";
@import "../../node_modules/bootstrap/scss/list-group";
@import "../../node_modules/bootstrap/scss/close";
@import "../../node_modules/bootstrap/scss/toasts";
@import "../../node_modules/bootstrap/scss/modal";
//@import "tooltip";
//@import "popover";
//@import "carousel";
@import "../../node_modules/bootstrap/scss/spinners";
// Helpers
@import "../../node_modules/bootstrap/scss/helpers";
// Utilities
@import "../../node_modules/bootstrap/scss/utilities/api";
// scss-docs-end import-stack

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,71 @@
$body-bg: #222222;
$dark: #292929;
$secondary: #343a40;
$accent: #b58e51;
$disabled: #455a64;
$custom-colors:(
"accent": $accent
);
$enable-shadows: true;
$btn-box-shadow: none;
$component-active-bg: $accent;
$input-color: $accent;
$input-bg: $secondary;
$input-border-color: $secondary;
$input-group-addon-bg: $disabled;
//$input-group-addon-color: $secondary;
$input-group-addon-border-color: $disabled;
$form-select-indicator-color: $accent;
$form-switch-color: $accent;
$form-switch-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-color}'/></svg>");
$form-check-input-border: 1px solid $accent !default;
$list-group-action-color: $accent;
$list-group-bg: $secondary;
$list-group-hover-bg: $secondary;
$list-group-action-hover-color: #adb5bd;
$alert-bg-scale: 0%;
$alert-border-scale: -10%;
$alert-color-scale: -100%;
// Configuration
@import "../../node_modules/bootstrap/scss/functions";
@import "../../node_modules/bootstrap/scss/variables";
$theme-colors: map-merge($theme-colors, $custom-colors);
@import "../../node_modules/bootstrap/scss/mixins";
@import "../../node_modules/bootstrap/scss/utilities";
//All other bootstrap imports
@import "bootstrap_imports.scss";
.modal-dialog-scrollable .modal-body {
&::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
border-radius: 10px;
background-color: $secondary;
}
&::-webkit-scrollbar {
width: 12px;
background-color: $dark;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
background-color: $accent;
}
}

5
public/css/style.css Normal file
View File

@ -0,0 +1,5 @@
.navbar {
background-color: #0091ea;
}
/*# sourceMappingURL=style.css.map */

1
public/css/style.css.map Normal file
View File

@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":["style.scss"],"names":[],"mappings":"AAAA;EACE","file":"style.css"}

3
public/css/style.scss Normal file
View File

@ -0,0 +1,3 @@
.navbar {
background-color: #0091ea;
}

15
routes/index.js Normal file
View File

@ -0,0 +1,15 @@
var express = require('express');
var router = express.Router();
const sequelize = require('../sequelize')
/* GET home page. */
router.get('/', function(req, res, next) {
sequelize.models.preset_cut.findAll({order: ['name']}).then((preset_cut)=>{
sequelize.models.preset_step_down_factor.findAll({order: ['name']}).then((step_down_factor)=>{
res.render('index', { preset_cut: preset_cut, step_down_factor: step_down_factor });
})
})
});
module.exports = router;

21
sequelize/index.js Normal file
View File

@ -0,0 +1,21 @@
const { Sequelize } = require('sequelize');
const logger = require('../config/winston')
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: 'db.sqlite',
logQueryParameters: true,
benchmark: true,
logging: msg => logger.debug.bind(msg)
});
const modelDefiners = [
require('./models/preset-cut.model'),
require('./models/preset-step-down-factor.model'),
require('./models/need-init.model')
];
for (const modelDefiner of modelDefiners) {
modelDefiner(sequelize);
}
module.exports = sequelize;

121
sequelize/init-db.js Normal file
View File

@ -0,0 +1,121 @@
const logger = require('../config/winston')
const sequelize = require('./index')
async function reset() {
logger.warn('Reset database...');
await sequelize.sync({ force: true });
logger.info('...Done');
logger.info('Populating with default value...');
await sequelize.models.preset_cut.bulkCreate([
{
name: "Wood, Plywood",
cut_speed: 500,
feed_by_tooth_more_1: 0.025,
feed_by_tooth_more_2: 0.03,
feed_by_tooth_more_3: 0.035,
feed_by_tooth_more_4: 0.06,
feed_by_tooth_more_5: 0.07,
feed_by_tooth_more_6: 0.09,
feed_by_tooth_more_8: 0.1,
},
{
name: "Hard Wood",
cut_speed: 450,
feed_by_tooth_more_1: 0.02,
feed_by_tooth_more_2: 0.025,
feed_by_tooth_more_3: 0.030,
feed_by_tooth_more_4: 0.055,
feed_by_tooth_more_5: 0.065,
feed_by_tooth_more_6: 0.085,
feed_by_tooth_more_8: 0.095,
},
{
name: "MDF",
cut_speed: 450,
feed_by_tooth_more_1: 0.050,
feed_by_tooth_more_2: 0.070,
feed_by_tooth_more_3: 0.100,
feed_by_tooth_more_4: 0.150,
feed_by_tooth_more_5: 0.200,
feed_by_tooth_more_6: 0.300,
feed_by_tooth_more_8: 0.400,
},
{
name: "Expanded PVC",
cut_speed: 300,
feed_by_tooth_more_1: 0.040,
feed_by_tooth_more_2: 0.060,
feed_by_tooth_more_3: 0.150,
feed_by_tooth_more_4: 0.200,
feed_by_tooth_more_5: 0.250,
feed_by_tooth_more_6: 0.350,
feed_by_tooth_more_8: 0.400,
},
{
name: "PMMA, PC, POM, ...",
cut_speed: 250,
feed_by_tooth_more_1: 0.015,
feed_by_tooth_more_2: 0.020,
feed_by_tooth_more_3: 0.025,
feed_by_tooth_more_4: 0.050,
feed_by_tooth_more_5: 0.060,
feed_by_tooth_more_6: 0.080,
feed_by_tooth_more_8: 0.090,
},
{
name: "Aluminum (2017A, 5083, ...) ",
cut_speed: 125,
feed_by_tooth_more_1: 0.010,
feed_by_tooth_more_2: 0.010,
feed_by_tooth_more_3: 0.010,
feed_by_tooth_more_4: 0.015,
feed_by_tooth_more_5: 0.015,
feed_by_tooth_more_6: 0.020,
feed_by_tooth_more_8: 0.030,
}
]);
await sequelize.models.preset_step_down_factor.bulkCreate([
{
name: "Soft",
k_less_2: 0.5,
k_more_2: 0.5,
k_more_3: 0.8,
k_more_4: 1,
k_more_5: 1,
k_more_6: 1
},
{
name: "Hard (Nonferrous) ",
k_less_2: 0.2,
k_more_2: 0.2,
k_more_3: 0.4,
k_more_4: 0.5,
k_more_5: 0.6,
k_more_6: 1
},
{
name: "Aluminium",
k_less_2: 0.1,
k_more_2: 0.1,
k_more_3: 0.2,
k_more_4: 0.25,
k_more_5: 0.35,
k_more_6: 0.35
},
])
await sequelize.models.need_init.create({name: 'init'})
logger.info('...Done')
}
async function check_database() {
await sequelize.sync();
let val = await sequelize.models.need_init.findAll({where: {name: 'init'}});
if(val.length === 0){
logger.info('Need init !');
await reset();
logger.info('Done')
}
}
exports.check_database = check_database;

View File

@ -0,0 +1,16 @@
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
sequelize.define('need_init', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
name: {
allowNull: false,
type: DataTypes.STRING
}
})
}

View File

@ -0,0 +1,48 @@
const { DataTypes } = require('sequelize');
module.exports = (sequelize) => {
sequelize.define('preset_cut', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
name: {
allowNull: false,
type: DataTypes.STRING
},
cut_speed: {
allowNull: false,
type: DataTypes.FLOAT,
},
feed_by_tooth_more_1: {
allowNull: false,
type: DataTypes.FLOAT,
},
feed_by_tooth_more_2: {
allowNull: false,
type: DataTypes.FLOAT,
},
feed_by_tooth_more_3: {
allowNull: false,
type: DataTypes.FLOAT,
},
feed_by_tooth_more_4: {
allowNull: false,
type: DataTypes.FLOAT,
},
feed_by_tooth_more_5: {
allowNull: false,
type: DataTypes.FLOAT,
},
feed_by_tooth_more_6: {
allowNull: false,
type: DataTypes.FLOAT,
},
feed_by_tooth_more_8: {
allowNull: false,
type: DataTypes.FLOAT,
},
})
}

View File

@ -0,0 +1,40 @@
const { DataTypes } = require('sequelize');
module.exports = (sequelize) =>{
sequelize.define('preset_step_down_factor',{
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
name: {
allowNull: false,
type: DataTypes.STRING
},
k_less_2: {
allowNull: false,
type: DataTypes.FLOAT,
},
k_more_2: {
allowNull: false,
type: DataTypes.FLOAT,
},
k_more_3: {
allowNull: false,
type: DataTypes.FLOAT,
},
k_more_4: {
allowNull: false,
type: DataTypes.FLOAT,
},
k_more_5: {
allowNull: false,
type: DataTypes.FLOAT,
},
k_more_6: {
allowNull: false,
type: DataTypes.FLOAT,
}
})
}

45
views/calculated.ejs Normal file
View File

@ -0,0 +1,45 @@
<div class="card bg-dark shadow-sm border-secondary">
<div class="card-header border-secondary border-bottom h3 text-center">
<%= __('Calculated Values') %>
</div>
<div class="card-body ">
<div class="row justify-content-center">
<div class="col-12 col-md-5">
<label for="rpm" class="form-label"><%= __('Spindle Speed') %></label>
<div class="input-group">
<input type="number" min="0" step="0.1" class="form-control bg-secondary" id="rpm" disabled
value="0">
<span class="input-group-text">RPM</span>
</div>
</div>
<div class="col-12 col-md-5 mt-3 mt-md-0">
<label for="feed_rate" class="form-label"><%= __('Feed Rate (mm/min)') %></label>
<div class="input-group">
<input type="number" min="0" step="0.1" class="form-control bg-secondary" id="feed_rate" disabled
value="0">
<span class="input-group-text">mm/min</span>
</div>
</div>
</div>
<div class="row mt-3 justify-content-center">
<div class="col-12 col-md-5">
<label for="step_down" class="form-label"><%= __('Step Down') %></label>
<div class="input-group">
<input type="number" min="0" step="0.1" class="form-control bg-secondary" id="step_down" disabled
value="0">
<span class="input-group-text">mm</span>
</div>
</div>
<div class="col-12 col-md-5 mt-3 mt-md-0">
<label for="feed_rate_sec" class="form-label"><%= __('Feed Rate (mm/sec)') %></label>
<div class="input-group">
<input type="number" min="0" step="0.1" class="form-control bg-secondary" id="feed_rate_sec"
disabled
value="0">
<span class="input-group-text">mm/sec</span>
</div>
</div>
</div>
</div>
</div>

33
views/constants.ejs Normal file
View File

@ -0,0 +1,33 @@
<div class="card bg-dark shadow-sm border-secondary">
<div class="card-header border-secondary border-bottom h3 text-center">
<%= __('Constants') %>
</div>
<div class="card-body ">
<div class="row justify-content-center">
<div class="col-12 col-md-5">
<label for="cutting_speed" class="form-label"><%= __('Cutting Speed') %></label>
<div class="input-group">
<input type="number" class="form-control bg-secondary" id="cutting_speed"
disabled value="0">
<span class="input-group-text">m/min</span>
</div>
</div>
<div class="col-12 col-md-5 mt-3 mt-md-0">
<label for="feed_tooth" class="form-label"><%= __('Feed By Tooth') %></label>
<div class="input-group">
<input type="number" class="form-control bg-secondary" id="feed_tooth" disabled
value="0">
<span class="input-group-text">mm</span>
</div>
</div>
</div>
<div class="row mt-3 justify-content-center">
<div class="col-12 col-md-5">
<label for="step_down_factor" class="form-label"><%= __('Step Down factor') %></label>
<input type="number" min="0" step="0.1" class="form-control bg-secondary" id="step_down_factor" disabled
value="0">
</div>
</div>
</div>
</div>

3
views/error.ejs Normal file
View File

@ -0,0 +1,3 @@
<h1><%= message %></h1>
<h2><%= error.status %></h2>
<pre><%= error.stack %></pre>

159
views/index.ejs Normal file
View File

@ -0,0 +1,159 @@
<!DOCTYPE html>
<html>
<head>
<title>CNC Speed Calculator</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='/css/style.css'/>
<link rel="stylesheet" href="/css/custom_bootstrap.css">
</head>
<body>
<nav class="navbar navbar-dark navbar-expand">
<div class="container">
<a href="#" class="navbar-brand">
CNC Speed Calculator
</a>
</div>
</nav>
<div class="container mt-4 text-white">
<div class="row">
<div class="col-12 col-lg-8 offset-lg-2">
<%- include('settings'); -%>
</div>
</div>
<div class="row mb-4 mt-lg-2">
<div class="col-12 col-lg-6 mt-3">
<%- include('calculated'); -%>
</div>
<div class="col-12 col-lg-6 mt-3">
<%- include('constants'); -%>
</div>
</div>
</div>
</body>
<script src="/js/bootstrap.min.js"></script>
<script>
let preset_cut = '<%- JSON.stringify(preset_cut) %>';
let preset_step_down_factor = '<%- JSON.stringify(step_down_factor) %>';
preset_cut = JSON.parse(preset_cut);
preset_step_down_factor = JSON.parse(preset_step_down_factor);
let preset_cut_elem = document.querySelector('#preset_cut');
let preset_step_down_factor_elem = document.querySelector('#preset_step_down_factor');
let tooth_nbr_elem = document.querySelector('#tooth_nbr');
let tool_diameter_elem = document.querySelector('#tool_diameter');
let max_rpm_elem = document.querySelector('#max_rpm')
let rpm_elem = document.querySelector('#rpm');
let feed_rate_elem = document.querySelector('#feed_rate');
let feed_rate_sec_elem = document.querySelector('#feed_rate_sec');
let step_down_elem = document.querySelector('#step_down');
let cutting_speed_elem = document.querySelector('#cutting_speed');
let feed_tooth_elem = document.querySelector('#feed_tooth');
let step_down_factor_elem = document.querySelector('#step_down_factor');
document.addEventListener("DOMContentLoaded", function (event) {
load();
onChange();
});
function onChange() {
if (preset_cut_elem.value === "" || preset_step_down_factor_elem.value === "" || tool_diameter_elem.value === "0" || tooth_nbr_elem.value === "0" || max_rpm_elem.value === "0") {
return;
}
save();
let new_preset_cut = getPresetValue(preset_cut, parseInt(preset_cut_elem.value, 10));
let new_preset_steep_down = getPresetValue(preset_step_down_factor, parseInt(preset_step_down_factor_elem.value, 10))
let new_tool_diameter = parseFloat(tool_diameter_elem.value);
let new_tooth_nbr = parseInt(tooth_nbr_elem.value);
let max_rpm = parseInt(max_rpm_elem.value);
let rpm = (1000 * new_preset_cut.cut_speed) / (3.14 * new_tool_diameter);
if (rpm > max_rpm)
rpm = max_rpm;
let feed_rate = rpm * getFeedByTooth(new_tool_diameter, new_preset_cut) * new_tooth_nbr;
let feed_rate_sec = feed_rate / 60;
let step_down = new_tool_diameter * getStepDownFactor(new_tool_diameter, new_preset_steep_down);
rpm = Math.round(rpm);
feed_rate = Math.round(feed_rate);
feed_rate_sec = feed_rate_sec.toFixed(2)
step_down = step_down.toFixed(2);
rpm_elem.value = rpm;
feed_rate_elem.value = feed_rate;
feed_rate_sec_elem.value = feed_rate_sec;
step_down_elem.value = step_down;
cutting_speed_elem.value = new_preset_cut.cut_speed;
feed_tooth_elem.value = getFeedByTooth(new_tool_diameter, new_preset_cut);
step_down_factor_elem.value = getStepDownFactor(new_tool_diameter, new_preset_steep_down);
}
function getPresetValue(list, id) {
for (let elem of list) {
if (elem.id === id) {
return elem;
}
}
return null
}
function getFeedByTooth(tool_diam, preset) {
if (tool_diam >= 8)
return preset.feed_by_tooth_more_8;
if (tool_diam >= 6)
return preset.feed_by_tooth_more_6;
if (tool_diam >= 5)
return preset.feed_by_tooth_more_5;
if (tool_diam >= 4)
return preset.feed_by_tooth_more_4;
if (tool_diam >= 3)
return preset.feed_by_tooth_more_3;
if (tool_diam >= 2)
return preset.feed_by_tooth_more_2;
return preset.feed_by_tooth_more_1;
}
function getStepDownFactor(tool_diam, preset) {
if (tool_diam >= 6)
return preset.k_more_6;
if (tool_diam >= 5)
return preset.k_more_5;
if (tool_diam >= 4)
return preset.k_more_4;
if (tool_diam >= 3)
return preset.k_more_3;
if (tool_diam >= 2)
return preset.k_more_2;
return preset.k_less_2;
}
function save(){
let values = {
preset_cut: preset_cut_elem.value,
preset_step_down_factor: preset_step_down_factor_elem.value,
tool_diameter: tool_diameter_elem.value,
tooth_nbr: tooth_nbr_elem.value,
max_rpm: max_rpm_elem.value
}
localStorage.setItem('previous_data', JSON.stringify(values));
}
function load(){
let previous_data = localStorage.getItem('previous_data')
if(previous_data === null)
return;
previous_data = JSON.parse(previous_data);
preset_cut_elem.value = previous_data.preset_cut;
preset_step_down_factor_elem.value = previous_data.preset_step_down_factor;
tool_diameter_elem.value = previous_data.tool_diameter;
tooth_nbr_elem.value = previous_data.tooth_nbr;
max_rpm_elem.value = previous_data.max_rpm;
}
</script>
</html>

49
views/settings.ejs Normal file
View File

@ -0,0 +1,49 @@
<div class="card bg-dark shadow-sm border-secondary">
<div class="card-header border-secondary border-bottom h3 text-center">
<%= __('Settings') %>
</div>
<div class="card-body ">
<div class="row">
<div class="col-12 col-md-6">
<label for="preset_cut" class="form-label"><%= __("Material") %></label>
<select class="form-select border-accent" id="preset_cut" onchange="onChange()">
<% preset_cut.forEach(function (preset){ %>
<option value="<%= preset.id %>"><%= preset.name %></option>
<% }) %>
</select>
</div>
<div class="col-12 col-md-6 mt-md-0 mt-3">
<label for="preset_step_down_factor" class="form-label"><%= __('Step down factor') %></label>
<select class="form-select border-accent" id="preset_step_down_factor" onchange="onChange()">
<% step_down_factor.forEach(function (preset){ %>
<option value="<%= preset.id %>"><%= preset.name %></option>
<% }) %>
</select>
</div>
</div>
<div class="row mt-3 justify-content-center">
<div class="col-6 col-md-3">
<label for="tooth_nbr" class="form-label"><%= __('Number of tooth') %></label>
<input type="number" min="1" step="1" class="form-control border-accent" id="tooth_nbr" onchange="onChange()"
onkeyup="onChange()" value="1">
</div>
<div class="col-6 col-md-3">
<label for="tool_diameter" class="form-label"><%= __('Tool Diameter') %></label>
<div class="input-group">
<input type="number" min="1" step="0.1" class="form-control border-accent border-end-0" id="tool_diameter" onkeyup="onChange()"
onchange="onChange()" value="3.0">
<span class="input-group-text border-accent">mm</span>
</div>
</div>
<div class="col-12 col-md-3 mt-3 mt-md-0">
<label for="max_rpm" class="form-label"><%= __('Spindle Max Speed') %></label>
<div class="input-group">
<input type="number" min="1000" step="1" class="form-control border-accent border-end-0" id="max_rpm" onkeyup="onChange()"
onchange="onChange()" value="30000">
<span class="input-group-text border-accent">RPM</span>
</div>
</div>
</div>
</div>
</div>