Заметки непрограммиста: как пишу программу. |
Диалог на производстве:
- Сделали проект для производства. Протестировали. Можно брать в работу?
- Не-е-ет.
- Почему?
- А надо еще, чтобы (по условию) результаты мастер проверял!
("Результаты" стоят в цехе, комп мастера стоит в бюро).
- Ладно... Давай так: система сама отследит наступление (события по условию) и на офисный принтер будет распечатывать задание на проверку? Мастер с заданием в цехе все проверит и потом в офисе на компе результат перенесет в систему?
- Давай попробуем.
Теперь надо браться за реализацию. С любого конца. Например, с этого:
Печать на офисный принтер
Хотелось бы следовать принципу, что вся конфигурация выполняется в программе и при замене, например, офисного принтера чтобы было достаточно указать его новый сетевой адрес.
Но есть еще драйверы принтеров и пришлось на сервере поставить CUPS и вывод на печать пустить через него:
Наступление события по условию
После обдумывания немногочисленных вариантов (например с триггером MySQL) остановился на очевидном: проверять условие в PHP-скрипте при каждой регистрации единицы готовой продукции в базе данных.
Оформление задания
Хочется просто и эстетично. PDF-тэмплэйт ? Посмотрел, как из PHP сделать PDF-документ. Остановился на проекте mPDF.
Нумерация задания
по порядку для каждой даты. Каждый день сначала. Формат: NN-YYMMDD
Педали
Надо добавить в панель управления возможность отключать эту проверку (1), менять параметр условия (2) и назначать принтер (3).
Запись в таблицу
Надо добавить в базу данных таблицу, в которой буду сохранять параметры задания (дата, номер, первый и последний проверяемый id) и отмечать выполнение (дата, кто проверял)
Запись в лог
"У меня все ходы записаны !" (Ильф, Петров. Двенадцать стульев)
Печать задания должна отображаться в логах.
Метки: PHP MySQL PDF mPDF CUPS Debian |
Tunnelblick, добавление route после подключения |
В Tunnelblick добавление route после подключения можно сделать просто, если в состав tblk-пакета добавить скрипт connected.sh:
#!/bin/bash
/sbin/route -n add address gateway
Цитата: "...scripts are run as root..."
См.здесь: https://code.google.com/p/tunnelblick/wiki/cUsingScripts
Метки: Tunnelblick OpenVPN Mac Apple |
COMPRESS (ZIP) FOLDER ON LINUX DEBIAN |
Compress (ZIP) folder on Linux Debian:
tar -czvf name.tar.gz folder/
Decompressing (unzipping) is here:
gunzip name.tar.gz
Источник: https://jarik.wordpress.com/2013/04/05/compress-zip-folder-on-linux-debian/
Метки: Linux tar gunzip |
Тюнинг web-сервера |
Получил письмо с жалобой, что один из производственных серверов стал вместо отчетов возвращать сообщение об ошибке:
"Fatal error: Maximum execution time of 30 seconds exceeded in ..."
Ну что ж, не хватает 30 секунд, установленных по умолчанию?
Не проблема, в файле php.ini увеличил значение параметра max_execution_time в php.ini до 60 секунд.
Но проблема осталась, только изменилось сообщение об ошибке:
"Fatal error: Uncaught exception 'MyException' with message 'Database query failed! Query: exec rep_period...' Error: Timeout expired ' in [filename]:37 Stack trace: #0 [internal function]: __lambda_func(1024, 'Database query ...', [filename]', 37, Array) #1 [filename](37): trigger_error('Database query ...') #2 [filename](41): db_execute(Object(com), 'exec rep_period...') #3 {main} thrown in [filename] on line 37"
Почитал документацию и понял, что действие max_execution_time не распространяется на обращения к базе данных. Значит надо смотреть способ подключения сервера к базе данных. В данном случае в код скрипта добавил строку:
$cnn->CommandTimeout=60;
и всё получилось.
Метки: apache php error |
HTML: передавать через SELECT массив значений |
Метки: HTML SELECT javascript |
XML_data = func(XML_схема, Excel_форма) |
Если надо подготовить XML-файл с данными для отправки куда-то, который надо подготовить руками и есть XML-схема (xsd-файл), то может выручить MS Excel.
Сначала в Excele проверяем включен ли риббон "Developer":
Затем подключим XML-схему в наш новый excel-файл:
Последнее, что осталось - драг&дропом замэпировать каждый элемент схемы с конкретной ячейкой листа таблицы, чтобы получилось нечто вроде такого:
В итоге получилась форма, которую можно набивать данными.
После заполнения сохраняем:
Проверяю результат в notepad:
Вижу то, что и требовалось.
Метки: Excel XML XSD |
Три флэшки - уже лаборатория. |
История первая: "Дебиан"
Давным давно начал проект, который планировал сделать на линуксе. Начал с конца и быстро-быстро соорудил эмулятор готового продукта. Чтобы проект можно было показать где угодно, то систему поставил на старую заслуженную 16Гб-флешку. Получилось надежно, неприхотливо и шустро. Ситстема запускается на любом неяблочном компьютере. Проект давно уже живет своей жизнью, а флешка - своей: у меня всегда под рукой рабочий линукс.
История вторая, про виртуализацию.
После того, как стал владельцем мощного мака, жизнь приобрела новые краски. Стал активно работать с виртуальными машинами, но стало жалко стремительно уменьшающегося обьема SSD-диска на маке. Принял решение: отселить виртуальные машины на флэшку (128Гб, usb 3.0).
Красота получилась!
История третья: лабораторная работа.
Не люблю (не допускаю) экспериментов на рабочей системе. Но и развивать систему, внедрять новые решения - надо. Поэтому взял еще одну флэшку, поставил на нее аналог рабочей системы (проект OpenFiler) и отладил метод подключения iSCSI-дисков к линукс-серверу с ОС Дебиан.
Важное примечание: во время лабораторных работ ни одна рабочая система не пострадала :)
Метки: USB флэшка ОС Debian OpenFiler |
На прощание с сайтом Gazeta.LV |
На прощание с сайтом Gazeta.LV.
Заодно, приглашаю к дискуссии.
А еще, сегодня подал заявку на доменное имя Gazeta2.lv
Пока - без какого либо плана.
И еще раз подтверждаю свою готовность поделиться архивом сайта по состоянию на 15 сентября.
Его размер - 25ГБ.
Вся суета - потом.
А сейчас время для музыки Энио Морриконе...
Метки: Gazeta.LV |
О Курске |
После просмотра этого фильма у меня не произошла переоценка ценностей. Нет желания обвинять и оправдывать. Может быть, стало чуть понятнее, что происходило тогда, когда каждая секунда, каждый удар сердца стучал в голове: курск, курск, курск...
118 погибшим военным морякам отдаю честь.
Метки: Патриотизм Курск Подводники Тайны |
Головоломка для себя: создание страницы из панелей. |
В связи с тем, что тачскрины удобны на производстве, то появилась потребность в определенном дизайне форм, который подразумевает надежное исполнение даже при нечетком попадании пальцами в кнопку.
С одной стороны, опыт подсказывает, что создать такую страницу задача банальная.
С другой стороны, каждый раз, когда собирался за нее взяться от нежелания сводило скулы.
Наконец, преодолев себя, взялся за дело...
формализовать создание подобной страницы для любого разрешения дисплея или размера окна броузера :
Составляю список параметров:
Точка отсчета - верхний левый угол экрана.
Далее, продолжаю работать в относительной системе измерения, т.е. за основу берется максимальный размер экрана как 100%
Положим, кол-во строк с панелями по вертикали Ny = 3 (как на рисунке)
Кол-во панелей по горизонтали N(1) =1; N(2) = 1; N(3) = 3
Левое поле (отступ) Oleft = Oright = Otop = Obottom = Oy = Ox = 5%
Высота панелей в строке H(1) = H(2) = H(3) = (100% - Otop - Obottom - (Ny -1) * Oy) / Ny = 80% / 3 = 26.6 %
Ширина панели W(1,1) = W(2,1) = 100% - Oleft - Oright = 90%
Ширина панели W(3,1) = W(3,2) = W(3,3) = (100% - Oleft - Oright - (Nx -1) * Ox) / Nx = 26.6%
Теперь пишу CSS-файл test.css:
body {
background: #99CCFF;
}
#panel_1_1{
position:absolute;
top: 5%;
left: 5%;
width: 90%;
height: 26.6%;
border-radius: 20px;
background: #FFFFCC;
}
#panel_2_1{
position:absolute;
top: 36.6%;
left: 5%;
width: 90%;
height: 26.6%;
border-radius: 20px;
background: #FFFFCC;
}
#panel_3_1{
position:absolute;
top: 68.2%;
left: 5%;
width: 26.6%;
height: 26.6%;
border-radius: 20px;
background: #FFFFCC;
}
#panel_3_2{
position:absolute;
top: 69%;
left: 36.6%;
width: 26.6%;
height: 26.6%;
border-radius: 20px;
background: #FFFFCC;
}
#panel_3_3{
position:absolute;
top: 69%;
left: 68.2%;
width: 26.6%;
height: 26.6%;
border-radius: 20px;
background: #FFFFCC;
}
И собственно html-файл test.html:
<html>
<head>
<link href="test.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="panel_1_1">
panel_1_1
</div>
<div id="panel_2_1">
panel_2_1
</div>
<div id="panel_3_1">
panel_3_1
</div>
<div id="panel_3_2">
panel_3_2
</div>
<div id="panel_3_3" onclick="alert('It work !');">
panel_3_3
</div>
</body>
</html>
Любую панель можно использовать как кнопку, чтобы вызывать событие. Для этого надо добавить инструкцию onclick в тег div.
Решение проверил в броузерах Firefox, Opera, Chrome и IE 8.0: только в IE страница отображается некорректно.
Чтобы контент в панелях распологался строго по центру, я в css-файл включил класс:
.cnt {
position:absolute;
text-align: center;
width: 100%;
top: 50%;
}
Естесственно, что надо не забыть в HTML-коде "обернуть" контент в тег DIV или SPAN с этим классом.
Если надо построить вариант посложнее:
то в этом случае предлагаю сл. принцип нумерации панелей:
Метки: HTML дизайн CSS |
Mikrotik. Шаг за шагом. |
Попробую систематизировать процесс настройки рутера "с нуля". Не столько для того, чтобы "как делать", сколь для того, чтобы настраивая очередной рутер не забыть что-либо настроить. Т.е. - это некий TODO-список. Как пиджак украшают пуговицы и карманы, так и я украсил этот список заголовками и картинками.
Начинаю настройку с того, что подключаюсь через WinBox и соглашаюсь на предлагаюмую конфигурацию:
Сначала открываю System - License и смотрю на номер версии, до которой позволено обновлять рутер:
Затем, открываю System - Packages и проверяю тип процессора (mipsbe) и текущую версию софта (5.24):
Пытливый взгляд разглядит эту же информацию в титульной строке на первом рисунке, но в свое оправдание скажу, что так было не всегда.
На сайте www.mikrotik.com проверяю доступную для обновления версию:
В моем случае - это routeros-mipsbe-6.19rc.npk. Скачиваю этот файл и загружаю на рутер. Обновление выполнится автоматически после перезагрузки рутера.
По пути System - Identify прописываю имя для рутера. Это имя будет отображаться в титульной строке программы и в имени бэкапных файлов.
В дефолтной конфигурации 1-й порт назначен для гэйтвея, 2-й порт - для мастер-порта свича LAN-сети.
Я предпочитаю "главные" порты разносить по краям, а порты между ними впоследствии гибко переназначать. Например, в моей практике бывают случаи, когда магистральный кабель надо подключить к двум приборам. В этом сдучае удобно 1-й и 2-й порты рутера обьединить бриждем, а 3,4,5-й порты оставить в LAN-свитче.
По умолчанию LAN-сети присвоен адрес 192.168.88.0
Если надо адрес LAN-сети изменить, то:
После изменения надо подключиться к рутеру уже по новому адресу.
Работа под учетной записью "admin" совсем нежелательна, т.к. ее хакеры пытаются взламывать в первую очередь. А то, что попытаются подбирать пароль к учетной записи "vecais" вероятность близка к нулю.
Поэтому в окне System - Users создаю новую учетную запись с полными правами и уникальным паролем.
Подключаюсь к рутеру под новым именем и сразу же удаляю учетную запись "admin"
На этом этапе желательно иметь подключение к интернету.
В окне IP - Firewall в закладке NAT правлю masquerade-запись:
Начинаю с создания в окне Wireless в закладке Security Prifiles нового профиля:
В окне Wireless в закладке Interfaces правлю имеющуюся запись. Все исправления делаю в закладке Wireless. Некоторые опции можно увидеть лишь нажав кнопку "Advanced Mode".
В окне IP - Firewall в закладке NAT добавляю masquerade-запись:
В окне Interfaces разблокирую интерфейс wlan1.
В окне IP - IPsec в закладке Proposals создаем новый proposal:
В окне IP - IPsec в закладке Peers создаем новый peer:
В окне IP - IPsec в закладке Policies создаем новую политику:
Чтобы пакеты вместо туннеля не уходили через NAT в окне IP -Firewall в закладке NAT правлю masquerade-запись: в поле Dst. Addresses вписываю адрес vpn-сети, например "! 192.77.0.0/24"
Основные шаги описаны.Тему можно было бы закрыть, но... может быть все это описать скриптом? Например, заполнить некий формуляр исходными данными, пустить скрипт и вуаля? Наверное, про это - в другой раз.
Метки: Mikrotik |
FireBird. Первый успех. |
Уже есть навыки работы с разными б/д, но c firebird-ом не получалось.
И, вот первый заработавший вариант!
И, кроме того, поправил сервер:
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<style>
html, body {
margin:0px;
padding:0px;
font-family: Arial, Helvetica, sans-serif;
text-align: center;
background-color:#ffffff;
font-size:12px;
}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
text-align: center;
}
table tbody tr:nth-child(odd) td {
background-color: #f2f2f2;
}
table th {
background-color:blue;
color:white;
}
</style>
</head>
<body>
<center>
<h2>Jantar, lietotāju saraksts</h2><br>
<?php
try {
$str_conn ="firebird:dbname=192.168.1.1/3050:C:\Program Files\Jantarik\DB.GDB";
$conn = new PDO($str_conn, "SYSDBA", "masterkey");
}
catch (PDOException $e) {
echo $e->getMessage();
}
try {
$sql = "SELECT * FROM USERS ORDER BY CODE";
echo "<table>";
echo '<tr><th>CODE</th><th>NAME</th><th>DEPARTMENT</th><th>CARD Nr.</th></tr>';
foreach($conn->query($sql) as $row) {
echo '<tr><td>'.number_format($row['CODE'],0,'','').'</td><td>'.$row['NAME'].'</td><td>'.$row['DEPARTMENT'].'</td><td>'.number_format($row['CARD'],0,'','').'</td></tr>';
}
echo "</table>";
} catch(PDOException $ex) {
//handle me.
}
?>
</body>
</html>
|
OpenVPN сервер на Mikrotik-е. Конспект. |
1. Сначала генерируем сертификаты. Мне удобно генерировать в модуле OpenVPN Administration Webmin-а:
- в Certification Authority List создаю новую Certification Authority и там же генерирую серверный и клиентский сертификат;
- экспортирую сертификаты на рабочий комп.
2. Далее работаю с Mikrotik-ом:
- update !
- импортирую сертификаты ca и собственно сервера. импортирую ключ сервера через путь System - Certificates, кнопка Import.
- IP - Pool: создаю пул адресов для туннельных клиентов. Например: 10.123.123.2-10.123.123.100
- PPP - закладка Profiles: создаю open-vpn профиль. В закладке General присваиваю имя, адрес и указываю созданный выше пул адресов. Например: Name=ovpn-profile, Local address=10.123.123.254, Remote address=vpn-pool. В закладке Protokols отмечаю Use encription=required
- PPP - закладка Interface - кнопка OVPN Server:
- PPP - закладка Secrets: добавляю имя клиента и, при необходимости, пароль.
3. Конфигурирую клиента.
Мой файл mikrotik.ovpn:
client
dev tap
proto tcp
remote WAN-address 1191
resolv-retry infinite
nobind
ns-cert-type server
persist-key
persist-tun
ca ca.crt
cert zanis.crt
key zanis.key
dh dh1024.pem
tls-client
port 1191
cipher AES-256-CBC
auth SHA1
auth-user-pass
pull
verb 3
route-up "route add LAN-address mask 255.255.255.0 10.123.123.254"
Метки: OpenVPN Mikrotik |
Любите ли вы костыли так, как люблю их я? |
На айпаде жены закончилось свободное место.
Айпад стал глючить, и ничего не позволил слить на хост.
Подключил проводом и синхронизация тут же навсегда убила последние и очень нужные видеозаписи.
(В этот момент я уже догадался, что танцор из меня никакой)
Как бы там ни было, а проблему надо решать: необходим переносной, внешний сторидж с очень понятным управлением.
Apple предлагает AirPort Time Capsule. Вариант непереносной и недешевый.
Тогда подумалось о внешнем WiFi кард-ридере типа Intenso Memory 2 Move Max:
У Verbatim есть его полный аналог (см. здесь)
Если в такой девайс поставить SD-карту побольше, то получится:
Чтобы не оставлять пользователя без интернета, в девайсе можно настроить сквозное подключение к WiFi рутеру.
Дополнено позже:
Данный девайс оказался удачным решением:
Метки: Intenso Memory 2 Move Max |
Паттерн MVC в языке PHP |
Вот и "дорос" до понимания паттернов (шаблонов). Интуитивно уже давно к этому шел и наконец прозрел. Идея хорошо изложена в этом видео:
Теперь разберусь в рабочем примере, взятом с YOUTUBE:
<html>
<head>
<style>
#login-controls {
margin: 0 auto;
border: 1px solid #ccc;
padding: 50px;
width: 300px;
}
.error-text{
color: #f00;
}
</style>
</head>
<body>
<div id="login-controls">
<h2>Login</h2>
<form method="POST" action="index.php">
<p>Username:<br />
<input type="text" name="user" />
</p>
<p>Password:<br />
<input type="password" name="pass" />
</p>
<input type="submit" name="op" value="login" />
</form>
</div>
</body>
</html>
Здесь все просто: обычный HTML-код для отображения формы. Заполненные поля передаются файлу index.php:
<?php
require_once('user_controller.inc.php');
require_once('user_model.inc.php');
@$op = $_REQUEST['op'];
$user_controller = new UserController();
switch($op){
case 'login':
$username = $_POST['user'];
$password = $_POST['pass'];
if ($user_controller -> login($username,$password)){
header("Location:main.php");
}else header("Location:login.php?err=1");
break;
case 'logout':
$user_controller -> logout();
header("Location:login.php");
break;
default:
header("Location:login.php");
break;
}
?>
В файле index.php:
Предусмотрены три варианта. Если переменная "op" имеет значение:
Разбираюсь с контроллером (обьект UserController) и смотрю файл user_controller.inc.php:
<?php
class UserController {
function UserController() //constructor
{
}
function create($username,$password,$email){
}
function login($username,$password){
if ($this->authenticate($username,$password)){
session_start();
$user = new UserModel($username);
$_SESSION['user']=$user;
return true;
}else{
return false;
}
}
static function authenticate($u,$p){
$authentic=false;
if($u=='admin' && $p =='admin') $authentic=true;
return $authentic;
}
function logout(){
session_start();
session_destroy();
}
}
?>
Класс содержит 4 метода:
Вернусь к рассмотрению файла index.php. Если метод login() контроллера $user_controller вернет значение "true", то загрузится страница main.php.
Файл main.php:
<?php
require_once('user_model.inc.php');
session_start();
if(!isset($_SESSION['user'])){
header("Location:login.php");
}else{
$user = $_SESSION['user'];
}
?>
<html>
<head>
</head>
<body>
<p>
You are now logged in <?php print $user->get_username()?>
...this is the main.
<a href="index.php?op=logout">Logout</a>
</p>
</body>
</html>
Чисто для демонстрации работы с данными через класс UserModel в файле main.php выводится имя пользоателя через метод get_username().
И, наконец, некоторый демо-вариант файла user_model.inc.php:
<?php
class UserModel{
private $username;
function UserModel($username){
$this->username = $username;
}
function get_username(){
return $this->username;
}
function set_username($username){
$this->username = $username;
}
}
?>
Для модели MVC (model-view-controller) назначение файлов такое:
В этом примере 2 точки входа: файлы index.php и login.php
Разбор примера закончен.
Использованные источники:
Метки: PHP MVC |
Двухсторонняя печать документации на принтере без дуплекса |
Ничего сложного, но чтобы каждый раз не заниматься исследованиями снова, запишу как.
Исходные условия:
Сначала печатаю все (Pages: All) четные страницы (Pages to Print: Odd only) в обратном порядке (Page Order: Reverse)
Затем, сохраняя ориентацию пачки напечатанных страниц, переворачиваю пачку через длинную сторону и возвращаю в лоток подачи бумаги
и печатаю все (Pages: All) нечетные страницы (Pages to Print: Even only) в прямом порядке (Page Order: Normal):
Получаю распечатку с правильным расположением листов и готовую к переплёту.
Алгоритм проверен и работает на компьютерах с ОС Windows с принтером HP LaserJet 2015.
Метки: печать документации Samsung SCX-4623FN |